ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] # file > 通常情况下, File 对象是来自用户在一个`<input>`元素上选择文件后返回的`FileList`对象,也可以是来自由拖放操作生成的`DataTransfer`对象,继承于`Blob` ![](https://box.kancloud.cn/7d87782dc69eb9ae1614333d6bff7dca_1494x436.png) * name:文件名,该属性只读。 * size:文件大小,单位为字节,该属性只读。 * type:文件的 MIME 类型,如果分辨不出类型,则为空字符串,该属性只读。 * lastModified:文件的上次修改时间,格式为时间戳。 * lastModifiedDate:文件的上次修改时间,格式为 Date 对象实例。 # blob > 一个 Blob对象表示一个不可变的, 原始数据的类似文件对象。Blob表示的数据不一定是一个JavaScript原生格式。 File 接口基于Blob,继承 blob功能并将其扩展为支持用户系统上的文件。 ## blob URL 关于`download`属性还有介绍: > 尽管 HTTP URL 需要位于同一源中,但是可以使用 blob: URL 和 data: URL ,以方便用户下载使用 JavaScript 生成的内容(例如使用在线绘图 Web 应用程序创建的照片)。 `Blob`(Binary Large Object)即二进制大对象,这个我们并不陌生,一些数据库将Blob用来表示存储二进制文件的字段类型。File 接口也是基于 Blob,继承了 Blob 的功能并将其扩展使其支持用户系统上的文件。Blob 对象通过 Blob 构造函数来创建: > Blob(blobParts\[, options\]) ~~~text var debug = {hello: "world"}; var blob = new Blob([JSON.stringify(debug, null, 2)], {type : 'application/json'}); ~~~ 如果需要实现一些简单的文本或 JS 字符串之类的文件下载,可以通过将文本信息转成 blob 二进制流,生成一个 Blob URL,配合`download`属性完成下载,代码如下。 ~~~js const downloadText = (text, filename = '') { const a = document.createElement('a') a.download = filename const blob = new Blob([text], {type: 'text/plain'}) // text指需要下载的文本或字符串内容 a.href = window.URL.createObjectURL(blob) // 会生成一个类似blob:http://localhost:8080/d3958f5c-0777-0845-9dcf-2cb28783acaf 这样的URL字符串 document.body.appendChild(a) a.click() a.remove() } ~~~ 这种 Blob URL 与常见的 HTTP URL 有什么区别呢? > Blob URL / Object URL是一种伪协议,可以让Blob和File对象用作图像和二进制数据下载链接等URL源。 浏览器在内部通过`URL.createObjectURL()`创建一个对 Blob 或 File 对象的特殊引用,生成的 Blob URL 只能在浏览器本地的单个实例和同一会话中使用,并且这个 URL 对象会在页面退出的时候被浏览器释放。 因此 Blob URL**并不能指向一个服务器资源**,你无法在其它页面中打开它。同时由于编码格式有所差别,Blob URL 比起 Data URLs 所占的空间资源更少,性能也更好。 Blob 为 Web 开发提供了一个非常有用的功能:创建 Blob URL。将二进制数据封装为 Blob 对象,然后使用`URL.createObjectURL()`生成 Blob URL,由于Blob URL本身就是一个同源URL,可以使用该 URL 配合`download`解决跨域资源的下载以及命名问题。 ### 解决方案 通过 Blob 和 Fetch 可以解决跨域和文件命名的问题:使用`fetch`获取跨域资源返回一个blob 对象并生成一个 Blob URL,配合`<a>`标签的`download`属性触发下载,代码如下: ~~~js function download(href, filename = '') { const a = document.createElement('a') a.download = filename a.href = href document.body.appendChild(a) a.click() a.remove() } function downloadFile(url, filename='') { fetch(url, { headers: new Headers({ Origin: location.origin, }), mode: 'cors', }) .then(res => res.blob()) .then(blob => { const blobUrl = window.URL.createObjectURL(blob) download(blobUrl, filename) window.URL.revokeObjectURL(blobUrl) }) } ~~~ 此时可以正常的将跨域图片下载到本地了。 需注意的是跨域资源所在的服务器需要配置`Access-Control-Allow-Origin`信息,否则会得到一个大写的跨域报错。header 配置例如: ~~~text // 允许任何域名访问 header('Access-Control-Allow-Origin: *'); //指定域名访问 header('Access-Control-Allow-Origin: https://h5.ele.me'); ~~~ 目前这种方法还存在一些不足,例如浏览器会限制 Blob 数据大小不超过500M,在性能方面也会有所不足。 ## 预览文件 ~~~ <input type="file" onchange=onUpload2(this.files[0])> <div id="preview2"></div> function onUpload2(file) { var blob = new Blob([file]) // 文件转化成二进制文件 var url = URL.createObjectURL(blob);//转化成url if (/image/g.test(file.type)) { var img = document.createElement('img') img.src = url img.onload = function (e) { console.log(this.src) URL.revokeObjectURL(this.src); // 释放createObjectURL创建的对象 } document.getElementById('preview2').appendChild(img) } if (/video/g.test(file.type)) { var video = document.createElement('video') video.controls = true video.src = url document.getElementById('preview2').appendChild(video) video.onload = function (e) { URL.revokeObjectURL(this.src); // 释放createObjectURL创建的对象 } } } ~~~ # FileReader FileReader用来读取file或blob文件数据,基于文件大小不同,读取的过程为异步。 ## 属性 * FileReader.error 一个DOMException,表示在读取文件时发生的错误 。 * FileReader.readyState 表示FileReader状态的数字。取值如下: | 常量名 | 值 | 描述 | | --- | --- | --- | | EMPTY | 0 | 还没有加载任何数据 | | LOADING | 1 | 数据正在被加载 | | DONE | 2 | 已完成全部的读取请求 | * FileReader.result 文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。 ## 事件 * FileReader.onload 处理load事件。该事件在读取操作完成时触发。 * FileReader.onloadend 处理loadend事件。该事件在读取操作结束时(要么成功,要么失败)触发。 * .... ## 方法 * FileReader.readAsArrayBuffer(file) 开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 * ArrayBuffer 数据对象. * FileReader.readAsBinaryString(file) 开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。 * FileReader.readAsDataURL(file) 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容。 * FileReader.readAsText(blob[, encoding]) 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。 ## 预览图片 ~~~ <input type="file" onchange=onUpload3(this.files[0])> <div id="preview3"></div> function onUpload3(file) { var img = document.createElement('img') var reader = new FileReader() reader.addEventListener("load", function () { console.log(reader.result) img.src = reader.result document.getElementById('preview3').appendChild(img) }, false); if (file) { reader.readAsDataURL(file) } } ~~~ # 格式差异 其实主要是两种格式base64和blob,它们之间的差异如下 * Blob URL的长度一般比较短 * Blob URL可以方便的使用XMLHttpRequest获取源数据, base64不是所有浏览器都支持 * Blob URL 只能在当前应用内部使用 # 格式之间转换 ## canvas转为blob对象 ~~~ canvas.toBlob(function (blobObj) { console.log(blobObj) }) 复制代码 ~~~ ## canvas转为base64 ~~~ let imgSrc = canvas.toDataURL('image/png') 复制代码 ~~~ ## base64转为blob ~~~ function dataURLtoBlob(dataurl) { let arr = dataurl.split(","); let mime = arr[0].match(/:(.*?);/)[1]; let bstr = atob(arr[1]); let n = bstr.length; let u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } ~~~ # 参考资料 [浅析 HTML5 中的 download 属性](https://zhuanlan.zhihu.com/p/58888918) [少侠,留步,图片预览术](https://juejin.im/post/5b890c386fb9a019c771713a#heading-7) [FileReader - Web API 接口参考 | MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader)