前言
作为一名前端,在工作中会遇到很多二进制处理的问题。比如文件的上传、下载,都离不开对(File、Blob)数据的处理,但是大部分人对于这些都没有一个清晰的认识。
本篇文章总结了浏览器端的二进制以及有关数据之间的转化,整体内容有:
如何得到数据
认识File、Blob
如何对数据处理转换
转换后数据相互之间转换
如何将处理后的数据进行还原
为了更好的理解,总结了一张概括图,如下所示:
下面针对每一个部分进行实践操作。
1. 输入
通常我们得到File
和Blob
对象,总结有这几种方式:
通过input标签
拖拽方式
canvas获取
接口返回
如图:
1.1 input
代码如下:
<input type="file" name="file" id="file"> const inputNode = document.getElementById('file') inputNode.addEventListener('change', (e) => { const file = e.target.files[0] })
得到File对象:
1.2 拖拽
代码如下:
<div id="drag"></div> const dragNode = document.getElementById("drag"); dragNode.ondragover = (e) => { e.preventDefault(); }; dragNode.ondrop = (e) => { e.preventDefault(); const files = e.dataTransfer.files; console.log(files); };
结果如下:
1.3 canvas
代码如下:
<canvas id="canvas"></canvas> const canvas = document.getElementById("canvas"); canvas.toBlob((blob) => { console.log("打印***blob", blob); });
结果如下:
1.4 接口
接口获取需要设置响应头为blob
,这里以axios和fetch为例:
axios:
axios.post(url,{ // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' responseType: 'blob', })
fetch
fetch(url).then(res=>{ // 常用的有 json、text、arrayBuffer、blob return res.blob() })
2 数据
经过输入得到File
或者Blob
,那么两者是什么,有什么关系,之间如何转换。
2.1 Blob
Blob
全称是binary large object
,即二进制大对象,它是 JavaScript 中的一个对象,表示原始的类似文件的数据。
Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
实际上,Blob 对象是包含有只读原始数据的类文件对象。简单来说,Blob 对象就是一个不可修改的二进制文件。
2.1.1 创建blob
可以使用 Blob() 构造函数来创建一个 Blob:
new Blob(array, options);
有两个参数:
array
:由ArrayBuffer
、ArrayBufferView
、Blob
、DOMString
等对象构成的,将会被放进Blob
;options
:可选的BlobPropertyBag
字典,它可能会指定如下两个属性type
:默认值为 "",表示将会被放入到blob
中的数组内容的 MIME 类型。endings
:默认值为"transparent
",用于指定包含行结束符\n
的字符串如何被写入,不常用。
常见的 MIME 类型如下:
类型 | 描述 |
---|---|
text/plain | 纯文本文档 |
text/html | HTML文档 |
text/javascript | JavaScript文件 |
text/css | CSS文件 |
application/json | JSON文件 |
application/pdf | PDF文件 |
application/xml | XML文件 |
image/jpeg | JPEG图像 |
image/png | PNG图像 |
image/svg+xml | SVG图像 |
audio/mpeg | MP3文件 |
video/mpeg | MP4文件 |
2.2 File
File
对象提供有关文件的信息,并允许网页中的 JavaScript 访问其内容。实际上,File 对象是特殊类型的 Blob,且可以用在任意的 Blob 类型的 context 中。Blob 的属性和方法都可以用于 File 对象。
注意:File 对象中只存在于浏览器环境中,在 Node.js 环境中不存在。
在 JavaScript 中,主要有两种方法来获取 File 对象:
<input>
元素上选择文件后返回的 FileList 对象;文件拖放操作生成的
DataTransfer
对象;
2.3 总结
File
是一种特殊的Blob
,只存在于浏览器中,继承Blob
中的方法和属性。两者如何转换
File -> Blob:因为File是继承于Blob,不用进行转换
Blob -> File:通过
new File([blob],fileName,{type:MIME})
3. 转换
通过Blob对象可以如何转换成自己想要的数据类型,有两种方式:
使用
createObjectURL
,生成Object URL
使用
FileReader
,生成DataURL
、BinaryString
、Text
、ArrayBuffer
下面分别实操下:
3.1 createObjectURL
URL
接口的 createObjectURL()
静态方法创建一个用于表示参数中给出的对象的 URL 的字符串。
URL 的生命周期与其创建时所在窗口的 document
绑定在一起。新对象 URL 代表指定的 File
对象或 Blob
对象。要释放对象 URL,需调用 revokeObjectURL()
。
无论是File
还是Blob
,可以直接调用URL.createObjectURL
两者的结果是相同的。
使用同一张图片进行转换为例:
File
通过input标签获取file对象,再进行转换:
<input type="file" name="file" id="file" /> const inputNode = document.getElementById("file"); inputNode.addEventListener("change", (e) => { const file = e.target.files[0]; console.log( "打印***file", URL.createObjectURL(file) ); })
结果:
输入浏览器预览:
Blob
使用canvas的toBlob
方法得到blob,再进行转换
<img src="./axios.png" alt="" srcset="" id="img" /> const cvs = document.createElement("canvas"); const imageNode = document.getElementById("img"); const ctx = cvs.getContext("2d"); cvs.width = imageNode.width; cvs.height = imageNode.height; ctx.drawImage(imageNode, 0, 0, cvs.width, cvs.height); cvs.toBlob((blob) => { console.log( "打印***blob", URL.createObjectURL(blob) ); });
结果:
输入浏览器预览:
3.1.1 Object URL
Object URL又称Blob URL(W3C定义名称),是HTML5中的新标准。它是一个用来表示File Object 或Blob Object 的URL。在网页中,我们可能会看到过这种形式的 blob: URL
其实Blob URL/Object URL 是一种伪协议,允许将 Blob 和 File 对象用作图像、二进制数据下载链接等的 URL 源。
对于 Blob/File 对象,可以使用 URL构造函数的 createObjectURL()
方法创建将给出对象的 URL。这个 URL 对象表示指定的 File 对象或 Blob 对象。我们可以在<img>
、<script>
标签中或者 <a>
和 <link>
标签的 href
属性中使用这个 URL。
经典下载代码:
// 使用 URL.createObjectURL 创建一个 URL 对象 const url = URL.createObjectURL(blob); // 创建一个 <a> 标签并设置下载属性 const a = document.createElement('a'); a.href = url; a.download = 'hello.txt'; // 设置下载的文件名 // 模拟点击 <a> 标签 a.style.display = 'none'; document.body.appendChild(a); a.click(); // 移除 <a> 标签并释放 URL 对象 document.body.removeChild(a); URL.revokeObjectURL(url);
3.2 FileReader
FileReader
允许异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File
或 Blob
对象指定要读取的文件或数据。
3.2.1 Data URL
Data URL
是一种包含数据的URL,其中数据部分通常是使用Base64
编码的。是一种将小数据嵌入到文档中的方式。它的格式如下:
data:[<mediatype>][;base64],<data>
<mediatype>
:表示数据的媒体类型(MIME 类型),例如text/plain
或image/png
。如果省略,默认值是text/plain;charset=US-ASCII
。;base64
:表示数据使用 Base64 编码。如果省略,数据部分应为 URL 编码的字符串。<data>
:表示实际的数据部分。
3.2.1.1 与Base64的关系
Base64 是一种基于64个可打印字符来表示二进制数据的表示方法。Base64 编码普遍应用于需要通过被设计为处理文本数据的媒介上储存和传输二进制数据而需要编码该二进制数据的场景。这样是为了保证数据的完整并且不用在传输过程中修改这些数据。
在 JavaScript 中,有两个函数被分别用来处理解码和编码 base64 字符串:
atob()
:解码,解码一个 Base64 字符串;btoa()
:编码,从一个字符串或者二进制数据编码一个 Base64 字符串。
例如:一个包含纯文本 "Hello, World!",使用btoa
转换后得到的Data URL
可以表示为:
data:text/html;base64,SGVsbG8sIFdvcmxkIQ==
总结一下:
Data URL 是一种将数据嵌入到文档中的 URL 格式,其中数据部分通常使用 Base64 编码。
Base64 是一种将二进制数据转换为 ASCII 字符串的编码方式,用于在文本环境中传输二进制数据。
3.2.1.2 生成Data URL的两种方式
canvas生成DataURL
const dataUrl = canvas.toDataURL();
结果如下:
FileReader的readAsDataURL
const reader = new FileReader(); // Read as Data URL reader.readAsDataURL(file); reader.onload = function (e) { console.log("打印***e,reader", e, reader); }
结果如下:
注意:使用e或者reader实例都可获取结果
3.2.2 Text
const reader = new FileReader(); // Read as Data URL reader.readAsText(file); reader.onload = function () { console.log("打印***text", reader.result); }
新建txt文件,写入this is a text
。 结果如下:
注意:如果直接读取图片或其他文件,乱码
3.2.3 BinaryString
const reader = new FileReader(); // Read as Binary String reader.readAsBinaryString(file); reader.onload = function () { console.log("打印***binaryString", reader.result); }
结果如下:
3.2.4 ArrayBuffer
const reader = new FileReader(); // Read as ArrayBuffer reader.readAsArrayBuffer(file); reader.onload = function () { console.log("打印***binaryString", reader.result); }
结果如下:
4. 四种格式相互转换
通过FileReader
转换的四种数据,之间如何相互转换。以下是将 Data URL
、ArrayBuffer
、Binary String
和 Text
之间相互转换的具体实现:
4.1 Data URL转换其他格式
4.1.1 Data URL -> ArrayBuffer
function dataURLToArrayBuffer(dataURL) { const binaryString = atob(dataURL.split(',')[1]); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; }
4.1.2 Data URL -> Binary String
function dataURLToBinaryString(dataURL) { return atob(dataURL.split(',')[1]); }
4.1.3 Data URL -> Text
async function dataURLToText(dataURL) { const arrayBuffer = dataURLToArrayBuffer(dataURL); return arrayBufferToText(arrayBuffer); }
4.2 ArrayBuffer转换其他格式
4.2.1 ArrayBuffer -> Data URL
function arrayBufferToDataURL(arrayBuffer, mimeType = 'application/octet-stream') { const bytes = new Uint8Array(arrayBuffer); const binaryString = bytes.reduce((data, byte) => data + String.fromCharCode(byte), ''); return `data:${mimeType};base64,${btoa(binaryString)}`; }
4.2.2 ArrayBuffer -> Binary String
function arrayBufferToBinaryString(arrayBuffer) { const bytes = new Uint8Array(arrayBuffer); return bytes.reduce((data, byte) => data + String.fromCharCode(byte), ''); }
4.2.3 ArrayBuffer -> Text
function arrayBufferToText(arrayBuffer) { const decoder = new TextDecoder('utf-8'); return decoder.decode(new Uint8Array(arrayBuffer)); }
4.3 Binary String转换其他格式
4.3.1 Binary String -> ArrayBuffer
function binaryStringToArrayBuffer(binaryString) { const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; }
4.3.2 Binary String -> Data URL
function binaryStringToDataURL(binaryString, mimeType = 'application/octet-stream') { return `data:${mimeType};base64,${btoa(binaryString)}`; }
4.3.3 Binary String -> Text
function binaryStringToText(binaryString) { return decodeURIComponent(escape(binaryString)); }
4.4 Text转换其他格式
4.4.1 Text -> ArrayBuffer
function textToArrayBuffer(text) { const encoder = new TextEncoder(); return encoder.encode(text).buffer; }
4.4.2 Text -> Binary String
function textToBinaryString(text) { return unescape(encodeURIComponent(text)); }
4.4.3 Text -> Data URL
function textToDataURL(text, mimeType = 'text/plain') { const arrayBuffer = textToArrayBuffer(text); return arrayBufferToDataURL(arrayBuffer, mimeType); }
通过上述函数,可以实现 Data URL
、ArrayBuffer
、Binary String
和 Text
之间的相互转换。注意在处理不同编码格式和数据类型时,需要根据具体需求调整 TextDecoder
和 TextEncoder
的编码参数。
5. FileReader转换后四种格式转换为blob格式
要将 FileReader
转换后的四种格式(Data URL
、ArrayBuffer
、Binary String
和 Text
)转换为 Blob
格式,可以使用以下方法:
5.1 Data URL -> Blob
function dataURLToBlob(dataURL) { const byteString = atob(dataURL.split(',')[1]); const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; const ab = new ArrayBuffer(byteString.length); const ia = new Uint8Array(ab); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], { type: mimeString }); }
5.2 ArrayBuffer -> Blob
function arrayBufferToBlob(arrayBuffer, mimeType = 'application/octet-stream') { return new Blob([arrayBuffer], { type: mimeType }); }
5.3 Binary String -> Blob
function binaryStringToBlob(binaryString, mimeType = 'application/octet-stream') { const ab = new ArrayBuffer(binaryString.length); const ia = new Uint8Array(ab); for (let i = 0; i < binaryString.length; i++) { ia[i] = binaryString.charCodeAt(i); } return new Blob([ab], { type: mimeType }); }
5.4 Text -> Blob
function textToBlob(text, mimeType = 'text/plain') { return new Blob([text], { type: mimeType }); }
具体示例:
// Data URL to Blob const dataURL = 'data:text/plain;base64,SGVsbG8gd29ybGQ='; const blobFromDataURL = dataURLToBlob(dataURL); // ArrayBuffer to Blob const arrayBuffer = new Uint8Array([72, 101, 108, 108, 111]).buffer; const blobFromArrayBuffer = arrayBufferToBlob(arrayBuffer); // Binary String to Blob const binaryString = 'Hello'; const blobFromBinaryString = binaryStringToBlob(binaryString); // Text to Blob const text = 'Hello world'; const blobFromText = textToBlob(text);
总结
通过上述整理可得,对于从文件到数据的转换可以形成一个完整的闭环。从文件-> 输入-> 数据 -> 转换能够形成一个直观的感受。