首页/文章/javascript

Blob、File 、DataURL(Base64)、BlobURL 之间的类型闭环

2024-03-07
16676 分钟
...

相信在工作中经常遇到,文件上传、图片压缩、文件下载、大文件断点续传,等等关于 js 来操作文件的需求。那么你真的了解文件类型之间的转换关系吗?如下:

  • Blob --> File
  • File --> DataURL(base64)
  • File --> BlobURL
  • HTTPURL| DataURL | BlobURL --> Blob

Blob 类型

Blob 类型是 File 文件类型的父类,它表示一个不可变、原始数据的类文件对象

如何得到 blob 对象?

1. new Blob(array, options)

let hiBlob = new Blob(\[`<h1>Hi gauseen!<h1>`\], { type: 'text/html' })

如上代码,就创建了一个 blob 对象,并声明了 text/html 类型 ,就像是创建一个 .html 文件。只不过它存在于浏览器的内存里。

2. fetch(url)

js 为我们提供了很多获取资源的 api,如:<img> 和 <script>
Fetch API 提供了一个获取资源的统一接口(包括跨域请求)

关于 fetch(url, options), url 参数支持格式有:

截止到 2020-01-13

  • http、https

  • blobURL: 比如通过 URL.createObjectURL() 获得

    // blobURL 示例:
    blob:null/7025638d-c05f-4c75-87d6-470a427e9aa3
  • dataURL: 如图片的 base64 格式,比如通过 convasElement.toDataURL() 获得

    // dataURL(base64) 黑色 1 像素示例:
    data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=

fetch(url, options) 响应数据可被解析成:

  • res.arrayBuffer(): 通用、固定长度的原始二进制数据缓冲区
  • res.blob(): Blob 类型
  • res.formData(): 表单数据类型
  • res.json(): JSON 格式
  • res.text(): 文本格式

本文主要关心 blob 类型转换,如下代码,用 fetch api 获取图片资源的 blob 对象,
当然也可以获取其它类型的资源。如:.txt .html

fetch('http://eg.com/to/path/someImg.png')
  .then(res => res.blob())
  .then(blob => {
    console.log('blob: ', blob)
  })

3. canvasElement.toBlob(callback)

canvas 具有图像操作能力,支持将一个已有的图片作为图片源,来操作图像。

如下,通过 canvas 将图片资源转成 blob 对象

<body>
  <canvas width="100" height="100"></canvas>
</body>

<script> const $ = arg => document.querySelector(arg)
  let convas = $('canvas')
  
  (async () =\> {
    let imgUrl = 'http://eg.com/to/path/someImg.png'
    let ctx = convas.getContext('2d')
    let img = await fetchImg(imgUrl)
    
    ctx.drawImage(img, 0, 0)

    
    convas.toBlob((blob) => {
      console.log('blob: ', blob)
    })

    
    let dataURL = convas.toDataURL()
    console.log('dataURL: ', dataURL)
  })()

  
  function fetchImg (url) {
    return new Promise((resolve, reject) => {
      let img = new Image()
      
      img.crossOrigin = 'anonymous'
      img.src = url
      
      img.onload = () => {
        resolve(img)
      }
    })
  } </script>

注:

  • 如果图片没加载完,就调用 drawImage,canvas 绘制将失败,所以我们简单封装了 fetchImg 方法,确保图片资源加载完成后再开始绘制图片。

  • 由于 canvas 中的图片可能来自一些第三方网站。在不做处理的情况下,使用跨域的图片绘制时会污染画布,这是出于安全考虑。在“被污染”的画布中调用 toBlob() toDataURL() getImageData() 会抛出安全警告。

    解决方法:

    let img = new Image()
    
    
    
    img.crossOrigin = 'anonymous'
    img.src = 'to/path'

    本质就是解决跨域问题,也可以使用 nginx 做个代理来解决

  • blobslice(startIndex, endIndex) 方法,复制 blob 对象某片段,与 js 数组的 slice 方法类似,文件的断点续传功能就是利用了改特性。

File 类型

File 包含文件的相关信息,可以通过 js 来访问其内容

如何获取 file 对象?

1. new File(bits, name[, options])

let hiFile = new File(\[`<h1>Hi gauseen!<h1>`\], 'fileName', { type: 'text/html' })

let hiBlob = new Blob(\[`<h1>Hi gauseen!<h1>`\], { type: 'text/html' })
let hiFile = new File(\[ hiBlob \], 'fileName', { type: 'text/html' })

如上代码,通过 File 构造函数,创建一个 file 对象,与上面的提到的 blob 类似。可以将 blob 转成 file 类型,这意味着上面获取的 blob,可以转成 file 类型。

2. inputElement.files

通过 <input type="file"> 标签获取 file 对象

$('input').addEventListener('change', e => {
  let file = e.target.files\[0\]
  console.log('file: ', file)
})

3. DragEvent.dataTransfer.files

通过拖、放获取 file 对象

<body>
  <div id="output">
     将文件拖放到这里~
  </div>
</body>

<script> const $ = arg => document.querySelector(arg)
  let outputEle = $('#output')
  
  outputEle.addEventListener('dragover', dragEvent => {
    dragEvent.preventDefault()
  })
  
  outputEle.addEventListener('drop', dragEvent => {
    dragEvent.preventDefault()
    
    let files = dragEvent.dataTransfer.files
    console.log('drag files: ', files)
  }) </script>

4. canvasElement.mozGetAsFile()

注: 截止 2020-01-13,该方法仅支持火狐浏览器

let file = canvasElement.mozGetAsFile('imgName')

DataURL(base64)

DataURL,前缀为 data: 协议的 URL,可以存储一些小型数据

语法:data:[<mediatype>][;base64],<data>

如下,黑色 1 像素示例:

data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=

上面提到的 Blob File 类型,如何“消费”它们呢?接着向下看

1. FileReader

允许 Web 应用程序异步读取存储在用户计算机上的文件(blobfile)。

fileReader(someFile).then(base64 => {
  console.log('base64: ', base64)
})

function fileReader (blob) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader()
    reader.onload = (e) => {
      resolve(e.target.result)
    }
    reader.readAsDataURL(blob)
  })
}

2. convasElement.toDataURL()

可以通过 canvas 图像处理能力,将图片转成 dataURL 形式。在上面 Blob 部分讲解中,代码已实现。

BlobURL(ObjectURL)

BlobURL 也叫 ObjectURL,它可以让只支持 URL 协议的 Api(如:<a> <link> <img> <script>) 访问 fileblob 对象。
dynamic-import-polyfill 库也用到了其特性。

如下,生成 blobURLcreateObjectURL 方法创建从 URL 到 Blob 的映射关系。
如:blob:http://eg.com/550e8400-e29b-41d4-a716-446655440000

let blobURL = URL.createObjectURL(object)

如下,revokeObjectURL 方法撤消 blobURL 与 Blob 的映射关系,有助于浏览器垃圾回收,提示性能。

URL.revokeObjectURL(blobURL)

形成闭环

通过上面的一系列转换关系,可以知道:

blob --> file --> dataURL(base64) | blobURL --> blob

这样就形成了一个闭环,文章开头的思维导图很好的说明了之间的转换关系。

应用举例

文件下载

通过 a 标签实现下载,blob 或 file 对象。至于如何获取 blob 和 file 上面已经说得很清楚了。

function downloadFile1 (blob, fileName = 'fileName') {
  let blobURL = URL.createObjectURL(blob)
  let link = document.createElement('a')
  link.download = fileName
  link.href = blobURL
  link.click()
  
  URL.revokeObjectURL(blobURL)
}

function downloadFile2 (blob, fileName = 'fileName') {
  let blobURL = URL.createObjectURL(blob)
  window.location.href = blobURL
  
  URL.revokeObjectURL(blobURL)
}

压缩图片

compressImage('to/path/someImg.png', 0.6).then(base64 => {
  console.log('compressImage: ', base64)
})

async function compressImage (imgUrl, quality = 1, type = 'image/jpeg') {
  let imgEle = await fetchImg(imgUrl)
  let canvas = document.createElement('canvas')
  let cxt = canvas.getContext('2d')
  
  let { width, height } = imgEle
  canvas.setAttribute('width', width || 100)
  canvas.setAttribute('height', height || 100)

  cxt.drawImage(imgEle, 0, 0)
  return canvas.toDataURL(type, quality)
}

function fetchImg (url) {
  return new Promise((resolve, reject) => {
    let img = new Image()
    img.crossOrigin = 'Anonymous'
    img.src = url
    img.onload = () => {
      resolve(img)
    }
  })
}

总结

相信读完这篇文章以后,你会对文件类型之间的转换有更全方位的了解,其实还有很多像 ArrayBuffer 存储二进制数据相关的 Api 没有写到,因为平时用到的场景比较少,感兴趣的可以结合本文,去更深一步的探索。

参考

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2026-02-24
navigator.sendBeacon全指南
在前端开发中,埋点系统是必不可少的一环。我们经常需要在用户 关闭页面 、 刷新 或 跳转路由 时,向服务器发送最后一条统计数据(比如用户停留时长、页面跳出率)。 但这看似简单的需求,在实现时却危机四伏:请求发不出去?页面跳转卡顿?今天我们就来聊聊这个问题的终极解决方案 —— 。 一、 痛点与传统方案的挣扎 场景还原 当用户点击关闭按钮时,浏览器会触发生命周期事件( 或 )。如果我们直接使用普通的异步 AJAX ( 或 ) 发送请求,浏览...
学习
javascript2025-11-02
理解浏览器事件系统,从用户点击到事件对象的完整旅程
深入理解浏览器事件系统:从用户点击到事件对象的完整旅程 “当我点击页面按钮时,背后发生了什么?为什么回调函数能收到一个包含丰富信息的event对象?今天,让我们一起揭开浏览器事件系统的神秘面纱。” 一个令人困惑的现象 作为前端开发者,我们每天都在写这样的代码: 这段代码如此熟悉,以至于我们很少停下来思考:​ ​这个 对象到底从哪里来?它为什么能知道点击的精确坐标?为什么能识别是哪个元素被点击了?​ ​ 更神奇的是,当我们手动创建事件时:...
学习
javascript2025-10-01
实现大文件上传全流程详解
在日常开发中,大文件上传是个绕不开的坎——动辄几百 MB 甚至 GB 级的文件,直接上传不仅容易超时,还会让用户体验大打折扣。最近我用 Vue+Express 实现了一套完整的大文件上传方案,支持分片上传、断点续传、秒传和手动中。 一、先看效果:我们要实现什么? 先上核心功能清单,确保大家明确目标,知道我们要解决哪些实际问题: 大文件分片上传 :将文件切成固定大小的小片段分批上传,避免单次请求超时 秒传 :服务器已存在完整文件时,直接返...
学习
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-15
一张 8K 海报差点把首屏拖垮
你给后台管理系统加了一个「企业风采」模块,运营同学一口气上传了 200 张 8K 宣传海报。首屏直接飙到 8.3 s,LCP 红得发紫。 老板一句「能不能像朋友圈那样滑到哪看到哪?」——于是你把懒加载重新翻出来折腾了一轮。 解决方案:三条技术路线,你全踩了一遍 1\. 最偷懒:原生 一行代码就能跑,浏览器帮你搞定。 🔍 关键决策点 2020 年后现代浏览器全覆盖,IE 全军覆没。 必须写死 ,否则 CLS 会抖成 PPT。 适用场景...
学习
javascript2025-09-10
🚀 Web Worker让你的应用丝滑
🌟 引言 在日常的前端开发中,你是否遇到过这样的困扰: 大数据处理时页面卡死 :处理几万条数据时,页面直接卡成PPT,用户点击毫无反应 复杂计算阻塞UI :图片处理、数据分析等计算密集型任务让整个应用假死 文件上传/下载卡顿 :大文件操作时,其他功能完全无法使用 实时数据处理性能差 :WebSocket接收大量数据时,页面渲染严重滞后 今天分享6个Web Worker的核心技巧,让你的应用告别卡顿,用户体验丝滑如德芙! 💡 核心技巧...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录