做者: gauseen
原文: https://github.com/gauseen/blog
公众号【学前端】只搞技术,不搞广告文,肯定不关注一下?
相信在工做中常常遇到,文件上传、图片压缩、文件下载、大文件断点续传,等等关于 js 来操做文件的需求。那么你真的了解文件类型之间的转换关系吗?以下:javascript
Blob
--> File
File
--> DataURL(base64)
File
--> BlobURL
HTTPURL| DataURL | BlobURL
--> Blob
提示: 公众号回复 “file” 可得高清原图html
Blob
类型是 File
文件类型的父类,它表示一个不可变、原始数据的类文件对象前端
blob
对象?1. new Blob(array, options)java
let hiBlob = new Blob([`<h1>Hi gauseen!<h1>`], { type: 'text/html' })
如上代码,就建立了一个 blob
对象,并声明了 text/html
类型 ,就像是建立一个 .html
文件。只不过它存在于浏览器的内存里。jquery
2. fetch(url)nginx
js 为咱们提供了不少获取资源的 api,如:<img> 和 <script>
,Fetch API
提供了一个获取资源的统一接口(包括跨域请求)git
关于 fetch(url, options)
, url
参数支持格式有:github
截止到 2020-01-13
http、https
blobURL
: 好比经过 URL.createObjectURL()
得到web
// blobURL 示例: blob:null/7025638d-c05f-4c75-87d6-470a427e9aa3
dataURL
: 如图片的 base64 格式,好比经过 convasElement.toDataURL()
得到json
// 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
等
// 获取图片的 blob 对象 // 经过 http、https 获取 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 自执行函数 (async () => { let imgUrl = 'http://eg.com/to/path/someImg.png' let ctx = convas.getContext('2d') let img = await fetchImg(imgUrl) // 向 canvas 画布上下文绘制图片 ctx.drawImage(img, 0, 0) // 获取图片 blob 对象 convas.toBlob((blob) => { console.log('blob: ', blob) }) // 获取图片 dataURL,也是 base64 格式 let dataURL = convas.toDataURL() console.log('dataURL: ', dataURL) })() // 获取图片资源,封装成 promise 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() // 1. 增长 crossOrigin 属性,值为 anonymous // 含义:执行一个跨域请求,在请求头里加 origin 字段 // 2. 后端要返回 Access-Control-Allow-Origin 响应头来容许跨域 img.crossOrigin = 'anonymous' img.src = 'to/path'
本质就是解决跨域问题,也可使用 nginx
作个代理来解决
blob
有 slice(startIndex, endIndex)
方法,复制 blob 对象某片断,与 js 数组的 slice
方法相似,文件的断点续传功能就是利用了改特性。File
包含文件的相关信息,能够经过 js 来访问其内容
file
对象?1. new File(bits, name[, options])
// 1. 参数是字符串组成的数组 let hiFile = new File([`<h1>Hi gauseen!<h1>`], 'fileName', { type: 'text/html' }) // 2. blob 转 file 类型 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 上传文件时触发 change 事件 $('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') // ondragover 事件规定在何处放置被拖动的数据 outputEle.addEventListener('dragover', dragEvent => { dragEvent.preventDefault() }) // ondrop 事件放置文件时触发 outputEle.addEventListener('drop', dragEvent => { dragEvent.preventDefault() // DataEvent.dataTransfer 属性保存着拖拽操做中的数据 let files = dragEvent.dataTransfer.files console.log('drag files: ', files) }) </script>
4. canvasElement.mozGetAsFile()
注: 截止 2020-01-13
,该方法仅支持火狐浏览器
let file = canvasElement.mozGetAsFile('imgName')
DataURL,前缀为 data:
协议的 URL,能够存储一些小型数据
语法:data:[<mediatype>][;base64],<data>
以下,黑色 1 像素示例:
data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=
上面提到的 Blob
File
类型,如何“消费”它们呢?接着向下看
1. FileReader
容许 Web 应用程序异步读取存储在用户计算机上的文件(blob
或 file
)。
// 将 blob 或 file 转成 DataURL(base64) 形式 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
,它可让只支持 URL 协议的 Api(如:<a> <link> <img> <script>
) 访问 file
或 blob
对象。
dynamic-import-polyfill 库也用到了其特性。
以下,生成 blobURL
,createObjectURL
方法建立从 URL 到 Blob 的映射关系。
如:blob:http://eg.com/550e8400-e29b-41d4-a716-446655440000
// object 建立 URL 的 File 对象、Blob 对象或者 MediaSource 对象 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() // 释放 blobURL URL.revokeObjectURL(blobURL) } // 固然也能够经过 window.location.href 下载文件 function downloadFile2 (blob, fileName = 'fileName') { let blobURL = URL.createObjectURL(blob) window.location.href = blobURL // 释放 blobURL URL.revokeObjectURL(blobURL) }
// 压缩图片,图片质量为 0.6 compressImage('to/path/someImg.png', 0.6).then(base64 => { console.log('compressImage: ', base64) }) // imgUrl 图片地址 // quality 图片质量 0 ~ 1 之间 // type 压缩图片只支持,image/jpeg 或 image/webp 类型 // 返回 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') // 设置 canvas 宽高 let { width, height } = imgEle canvas.setAttribute('width', width || 100) canvas.setAttribute('height', height || 100) cxt.drawImage(imgEle, 0, 0) return canvas.toDataURL(type, quality) } // 经过 url 获取图片 function fetchImg (url) { return new Promise((resolve, reject) => { let img = new Image() img.crossOrigin = 'Anonymous' img.src = url img.onload = () => { resolve(img) } }) }
相信读完这篇文章之后,你会对文件类型之间的转换有更全方位的了解,其实还有不少像 ArrayBuffer
存储二进制数据相关的 Api 没有写到,由于平时用到的场景比较少,感兴趣的能够结合本文,去更深一步的探索。
欢迎关注无广告文章公众号:学前端
PS: 只搞技术不搞广告文都不关注?没天理啦!
你的关注是我更文最大动力!