前端二进制和文件那些事看这篇就够了!
一、思考
-
图片的src为什么有时候是 ”https://xxx...“,有时候是 ”data: image/xxx; base64, xxx...“,有时候又是 ”blob:https://xxx“ ?有什么区别?
-
处理文件时候经常会跟blob和FileReader打交道,它们到底是什么?有什么作用?
-
前端能否将文件先请求后缓存到本地,等用户想下载时点击按钮快速下载?
二、前置知识
在了解前端二进制相关知识前,先简单了解几个文件相关概念 File、FileList、FileReader。
File对象
File 是一个文件对象,用来存储文件的内容。(它基于Blob接口,这里先不用了解Blob是什么,后文会详细展开)
FileList
FileList 是一组文件对象,通常来自于HTML input 元素的 files 属性,简单说就是 [ File, File, File, ... ] 。
FileReader
FileReader 对象允许 Web 应用程序异步读取 File 或 Blob数据,以其他形式输出。
了解前置知识后,进入正文
三、前端二进制由来
随着浏览器的流行,人们会希望它可以运行复杂的3D应用程序,于是诞生了一种 javaScript API 叫 WebGL,然而早期版本的 WebGL 会因为js的数组在内存中的格式与原生数组之间不匹配,出现性能问题。为了解决这个问题,后来出现了 TypedArray(定型数组),让js运行时使用它可以分配、读取和写入数组,这个数组可以直接传给底层图形驱动程序 API,也可以直接从底层获取到。而这些定型数组引用的基本单位就是 ArrayBuffer(预分配内存)。
四、ArrayBuffer
介绍
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。数据在内存中是以二进制形式存放的,所以简单说ArrayBuffer就是一片内存。
创建方式
1.构造函数生成
const buf = new ArrayBuffer(16); // 在内存中分配 16 字节
这个 buf 对象所储存的就是我们在内存中分配给它的大小为16字节的内存,不过我们没法对它进行直接操作,必须通过视图 TypedArray 或 DataView 去操作它,这里就不详细展开。
2.fetch把请求的响应数据转化为ArrayBuffer
request(item,index){
fetch('/file',{
method: 'get',
responseType: 'arraybuffer'
}).then(res => {
return res.arrayBuffer(); // 响应内容转为ArrayBuffer
}).then(arraybuffer => {
//...
})
}
3.FileReader.readAsArrayBuffer()将文件读取为ArrayBuffer
const reader = new FileReader();
reader.onload = function(e) {
const arrayBuffer = reader.result;
}
reader.readAsArrayBuffer(file); // 文件读取结果返回ArrayBuffer
用法
当我们拥有一片内存空间后怎么使用呢?这时候就涉及到 blob(类文件对象)。
五、Blob
介绍
Blob 对象表示一个不可变、原始数据的类文件对象。简单说,Blob构造函数能把 ArrayBuffer对象储存的内存空间包装成一个新的对象,如下:
const buf = new ArrayBuffer(16);
const blob = new Blob([buf]);
这个blob对象就可以进行本地读取文件、文件下载等操作。同时,Blob构造函数还能把DOMString对象也做同样的包装,如下:
const fileParts = ['<div>DOMString</div>']; // 一个包含 DOMString 的数组
const blob = new Blob(fileParts, {type : 'text/html'}); // 得到 blob
当我们拥有一个类文件对象后有什么用?一般用法见下文
用法
本地读取文件
最常用的方式是转化为链接后本地访问,将blob转发为可访问的链接地址,在浏览器中访问
1.FileReader.readAsDataURL()将blob转为Data URL
const fileParts = ['<div>DOMString</div>']; // 一个包含 DOMString 的数组
const blob = new Blob(fileParts, {type : 'text/html'}); // 得到 blob
const reader = new FileReader();
reader.onload = function(e) {
const dataUrl= reader.result;
console.log(dataUrl)// 输出 'data:text/html;base64,PGRpdj5ET01TdHJpbmc8L2Rpdj4='
}
reader.readAsDataURL(blob); // 将blob对象转为Data URL
这个 Data URL 由 data: 协议 + MIME 类型 + blob转为base64的编码 组成,我们可以访问下这个链接,如下:
2.URL.createObjectURL()将blob转为Object URL
const fileParts = ['<div>DOMString</div>']; // 一个包含 DOMString 的数组
const blob = new Blob(fileParts, {type : 'text/html'}); // 得到 blob
const objectURL = URL.createObjectURL(blob)
console.log(objectURL) // 输出 'blob:null/890d9407-829b-4333-97c5-fb05d5938dbf'
这个 Object URL格式为 blob: + origin + uuid,同样的,我们可以访问下这个链接,如下:
不过 Object URL相比Data URL来说,它是一个临时的链接,当我们把当前文档关掉或者使用URL.revokeObjectURL()清除url对blob的映射, Object URL就会访问不了,会从浏览器中收到 404 错误,如下:
文件下载
Data URL 和 Object URL 都可以利用a标签,将本地访问链接对应的文件内容下载到本地
// 创建类文件对象
const fileParts = ['<div>DOMString</div>'];
const blob = new Blob(fileParts, {type : 'text/html'});
// 转化为本地访问链接
const objectURL = URL.createObjectURL(blob)
// 使用a标签将文件下载到本地
const a = document.createElement('a');
a.style.display = 'none';
a.href = objectURL ;
a.download = name;
document.body.appendChild(a);
a.click();
// 触发下载行为后,记得清除a标签和Object URL的映射,释放内存
document.body.removeChild(a);
URL.revokeObjectURL(objectURL);
如图:
下载后打开文件看看,如下:
六、总结
不管是从本地读取下载文件,还是从远程请求获取文件,前端都可以通过 ArrayBuffer、Blob、File、FileReader 这些对象之间的灵活转换和处理,让文件以二进制数据流的形式保存在内存中,之后面对不同场景做不同的文件操作。
转载自:https://juejin.cn/post/7201332537338822712