相信在工作中经常遇到,文件上传、图片压缩、文件下载、大文件断点续传,等等关于 js 来操作文件的需求。那么你真的了解文件类型之间的转换关系吗?如下:
Blob
—>File
File
—>DataURL(base64)
File
—>BlobURL
HTTPURL| DataURL | BlobURL
—>Blob
Blob 类型
Blob
类型是 File
文件类型的父类,它表示一个不可变、原始数据的类文件对象
如何得到 blob
对象?
1. new Blob(array, options)
1let 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()
获得1// blobURL 示例: 2blob:null/7025638d-c05f-4c75-87d6-470a427e9aa3
-
dataURL
: 如图片的 base64 格式,比如通过convasElement.toDataURL()
获得1// dataURL(base64) 黑色 1 像素示例: 2data: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
等
1fetch('http://eg.com/to/path/someImg.png')
2 .then(res => res.blob())
3 .then(blob => {
4 console.log('blob: ', blob)
5 })
3. canvasElement.toBlob(callback)
canvas 具有图像操作能力,支持将一个已有的图片作为图片源,来操作图像。
如下,通过 canvas 将图片资源转成 blob
对象
1<body>
2 <canvas width="100" height="100"></canvas>
3</body>
4
5<script> const $ = arg => document.querySelector(arg)
6 let convas = $('canvas')
7
8 (async () =\> {
9 let imgUrl = 'http://eg.com/to/path/someImg.png'
10 let ctx = convas.getContext('2d')
11 let img = await fetchImg(imgUrl)
12
13 ctx.drawImage(img, 0, 0)
14
15
16 convas.toBlob((blob) => {
17 console.log('blob: ', blob)
18 })
19
20
21 let dataURL = convas.toDataURL()
22 console.log('dataURL: ', dataURL)
23 })()
24
25
26 function fetchImg (url) {
27 return new Promise((resolve, reject) => {
28 let img = new Image()
29
30 img.crossOrigin = 'anonymous'
31 img.src = url
32
33 img.onload = () => {
34 resolve(img)
35 }
36 })
37 } </script>
注:
-
如果图片没加载完,就调用
drawImage
,canvas 绘制将失败,所以我们简单封装了fetchImg
方法,确保图片资源加载完成后再开始绘制图片。 -
由于 canvas 中的图片可能来自一些第三方网站。在不做处理的情况下,使用跨域的图片绘制时会污染画布,这是出于安全考虑。在“被污染”的画布中调用
toBlob()
toDataURL()
getImageData()
会抛出安全警告。解决方法:
1let img = new Image() 2 3 4 5img.crossOrigin = 'anonymous' 6img.src = 'to/path'
本质就是解决跨域问题,也可以使用
nginx
做个代理来解决 -
blob
有slice(startIndex, endIndex)
方法,复制 blob 对象某片段,与 js 数组的slice
方法类似,文件的断点续传功能就是利用了改特性。
File 类型
File
包含文件的相关信息,可以通过 js 来访问其内容
如何获取 file
对象?
1. new File(bits, name[, options])
1let hiFile = new File(\[`<h1>Hi gauseen!<h1>`\], 'fileName', { type: 'text/html' })
2
3let hiBlob = new Blob(\[`<h1>Hi gauseen!<h1>`\], { type: 'text/html' })
4let hiFile = new File(\[ hiBlob \], 'fileName', { type: 'text/html' })
如上代码,通过 File
构造函数,创建一个 file
对象,与上面的提到的 blob
类似。可以将 blob 转成 file 类型,这意味着上面获取的 blob,可以转成 file 类型。
2. inputElement.files
通过 <input type="file">
标签获取 file
对象
1$('input').addEventListener('change', e => {
2 let file = e.target.files\[0\]
3 console.log('file: ', file)
4})
3. DragEvent.dataTransfer.files
通过拖、放获取 file
对象
1<body>
2 <div id="output">
3 将文件拖放到这里~
4 </div>
5</body>
6
7<script> const $ = arg => document.querySelector(arg)
8 let outputEle = $('#output')
9
10 outputEle.addEventListener('dragover', dragEvent => {
11 dragEvent.preventDefault()
12 })
13
14 outputEle.addEventListener('drop', dragEvent => {
15 dragEvent.preventDefault()
16
17 let files = dragEvent.dataTransfer.files
18 console.log('drag files: ', files)
19 }) </script>
4. canvasElement.mozGetAsFile()
注: 截止 2020-01-13
,该方法仅支持火狐浏览器
1let file = canvasElement.mozGetAsFile('imgName')
DataURL(base64)
DataURL,前缀为 data:
协议的 URL,可以存储一些小型数据
语法:data:[<mediatype>][;base64],<data>
如下,黑色 1 像素示例:
1data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=
上面提到的 Blob
File
类型,如何“消费”它们呢?接着向下看
1. FileReader
允许 Web 应用程序异步读取存储在用户计算机上的文件(blob
或 file
)。
1fileReader(someFile).then(base64 => {
2 console.log('base64: ', base64)
3})
4
5function fileReader (blob) {
6 return new Promise((resolve, reject) => {
7 let reader = new FileReader()
8 reader.onload = (e) => {
9 resolve(e.target.result)
10 }
11 reader.readAsDataURL(blob)
12 })
13}
2. convasElement.toDataURL()
可以通过 canvas 图像处理能力,将图片转成 dataURL 形式。在上面 Blob 部分讲解中,代码已实现。
BlobURL(ObjectURL)
BlobURL
也叫 ObjectURL
,它可以让只支持 URL 协议的 Api(如:<a> <link> <img> <script>
) 访问 file
或 blob
对象。
dynamic-import-polyfill 库也用到了其特性。
如下,生成 blobURL
,createObjectURL
方法创建从 URL 到 Blob 的映射关系。
如:blob:http://eg.com/550e8400-e29b-41d4-a716-446655440000
1let blobURL = URL.createObjectURL(object)
如下,revokeObjectURL
方法撤消 blobURL 与 Blob 的映射关系,有助于浏览器垃圾回收,提示性能。
1URL.revokeObjectURL(blobURL)
形成闭环
通过上面的一系列转换关系,可以知道:
1blob --> file --> dataURL(base64) | blobURL --> blob
这样就形成了一个闭环,文章开头的思维导图很好的说明了之间的转换关系。
应用举例
文件下载
通过 a 标签实现下载,blob 或 file 对象。至于如何获取 blob 和 file 上面已经说得很清楚了。
1function downloadFile1 (blob, fileName = 'fileName') {
2 let blobURL = URL.createObjectURL(blob)
3 let link = document.createElement('a')
4 link.download = fileName
5 link.href = blobURL
6 link.click()
7
8 URL.revokeObjectURL(blobURL)
9}
10
11function downloadFile2 (blob, fileName = 'fileName') {
12 let blobURL = URL.createObjectURL(blob)
13 window.location.href = blobURL
14
15 URL.revokeObjectURL(blobURL)
16}
压缩图片
1compressImage('to/path/someImg.png', 0.6).then(base64 => {
2 console.log('compressImage: ', base64)
3})
4
5async function compressImage (imgUrl, quality = 1, type = 'image/jpeg') {
6 let imgEle = await fetchImg(imgUrl)
7 let canvas = document.createElement('canvas')
8 let cxt = canvas.getContext('2d')
9
10 let { width, height } = imgEle
11 canvas.setAttribute('width', width || 100)
12 canvas.setAttribute('height', height || 100)
13
14 cxt.drawImage(imgEle, 0, 0)
15 return canvas.toDataURL(type, quality)
16}
17
18function fetchImg (url) {
19 return new Promise((resolve, reject) => {
20 let img = new Image()
21 img.crossOrigin = 'Anonymous'
22 img.src = url
23 img.onload = () => {
24 resolve(img)
25 }
26 })
27}
总结
相信读完这篇文章以后,你会对文件类型之间的转换有更全方位的了解,其实还有很多像 ArrayBuffer
存储二进制数据相关的 Api 没有写到,因为平时用到的场景比较少,感兴趣的可以结合本文,去更深一步的探索。