最近原创文章回顾😊:
- 《1.2w字 | 初中级前端 JavaScript 自测清单 - 1》
- 《了不起的 Webpack HMR 学习指南(含源码分析)》
- 《了不起的 Webpack 构建流程学习指南》
- 《你不知道的 WeakMap》番外篇
- 《你不知道的 Blob》番外篇
- 《了不起的 tsconfig.json 指南》
- 《200行JS代码,带你实现代码编译器》
学习章节:《你不知道的 Blob》
原文对 Blob 的知识点介绍得非常完整清晰,本文通过四个问题来总结本文核心知识:
- Blob 是什么?
- Blob 怎么用?
- Blob 有哪些使用场景?
- Blob 与 ArrayBuffer 有何区别?
一、Blob 是什么?
Blob(Binary Large Object)表示二进制类型的大对象,通常是影像、声音或多媒体文件。MySql/Oracle数据库中,就有一种Blob类型,专门存放二进制数据。在 JavaScript 中 Blob 对象表示一个不可变、原始数据的类文件对象,它不一定非得是大量数据,也可以表示一个小型文件的内容。
另外,JavaScript 中的 File 接口是基于 Blob,继承 Blob 的功能并将其扩展使其支持用户系统上的文件。
二、Blob 怎么用?
Blob
由一个可选字符串 type
和 blobParts
组成,其中, type
通常为 MIME 类型。
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,常见有:超文本标记语言文本 .html
text/html
、PNG图像 .pngimage/png
、普通文本 .txttext/plain
等。
1. 构造函数
Blob 构造函数语法为:
const myBlob = new Blob(blobParts[, options])
入参:
blobParts
:它是一个由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等对象构成的数组。DOMStrings 会被编码为 UTF-8。options
:一个可选的对象,包含以下两个属性:
type
:默认值为""
,表示将会被放入到 blob 中的数组内容的 MIME 类型。endings
:默认值为"transparent"
,用于指定包含行结束符\n
的字符串如何被写入。它是以下两个值中的一个:"native"
,代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者"transparent"
,代表会保持 blob 中保存的结束符不变。
出参:
返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。
2. 属性和方法
2.1 属性介绍
Blob
对象拥有 2 个属性:
size
:只读,表示Blob
对象中所包含的数据大小(以字节为单位);type
:只读,值为字符串,表示该Blob
对象所包含数据的 MIME 类型。若类型未知,则该属性值为空字符串。
2.2 方法介绍
slice([start[, end[, contentType]]])
:返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。stream()
:返回一个能读取 Blob 内容的ReadableStream
。text()
:返回一个 Promise 对象且包含 Blob 所有内容的 UTF-8 格式的USVString
。arrayBuffer()
:返回一个 Promise 对象且包含 Blob 所有内容的二进制格式的ArrayBuffer
。
注意:** Blob
对象是不可改变的**,但是可以进行分割,并创建出新的 Blob
对象,将它们混合到一个新的 Blob
中。类似于 JavaScript 字符串:我们无法更改字符串中的字符,但可以创建新的更正后的字符串。
3. 简单上手
3.1 示例1:从字符串创建 Blob
let myBlobParts = ['<html><h2>Hello Leo</h2></html>']; // 一个包含DOMString的数组 let myBlob = new Blob(myBlobParts, {type : 'text/html', endings: "transparent"}); // 得到 blob console.log(myBlob.size + " bytes size"); // Output: 31 bytes size console.log(myBlob.type + " is the type"); // Output: text/html is the type
3.2 示例2:从类型化数组和字符串创建 Blob
JavaScript类型化数组是一种类似数组的对象,并提供了一种用于 访问原始二进制数据的机制 。并且在类型数组上调用 Array.isArray()
会返回 false
。
详细可参考MDN《JavaScript 类型化数组》章节。
let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二进制格式的 "hello" let blob = new Blob([hello, ' ', 'leo'], {type: 'text/plain'}); // Output: "Hello leo"
3.3 示例3:组装新的 Blob
由于 Blob
对象是不可改变的,但我们可以进行分割,并组装成一个新的 Blob
对象:
let blob1 = new Blob(['<html><h2>Hello Leo</h2></html>'], {type : 'text/html', endings: "transparent"}); let blob2 = new Blob(['<html><h2>Happy Boy!</h2></html>'], {type : 'text/html', endings: "transparent"}); let slice1 = blob1.slice(16); let slice2 = blob2.slice(0, 16); await slice1.text(); // currtent slice1 value: "Leo</h2></html>" await slice2.text(); // currtent slice2 value: "<html><h2>Happy " let newBlob = new Blob([slice2, slice1], {type : 'text/html', endings: "transparent"}); await newBlob.text(); // currtent newBlob value: "<html><h2>Happy Leo</h2></html>"
三、Blob 有哪些使用场景?
1. 图片本地预览
这里整理 2 种图片本地预览的方式:
- 使用 DataURL 方式;
- 使用 Blob URL/Object URL 方式;
<body> <h1>1.DataURL方式:</h1> <input type="file" accept="image/*" onchange="selectFileForDataURL(event)"> <img id="output1"> <h1>2.Blob方式:</h1> <input type="file" accept="image/*" onchange="selectFileForBlob(event)"> <img id="output2"> <script> // 1.DataURL方式: async function selectFileForDataURL() { const reader = new FileReader(); reader.onload = function () { const output = document.querySelector("#output1") output.src = reader.result; } reader.readAsDataURL(event.target.files[0]); } //2.Blob方式: async function selectFileForBlob(){ const reader = new FileReader(); const output = document.querySelector("#output2"); const imgUrl = window.URL.createObjectURL(event.target.files[0]); output.src = imgUrl; reader.onload = function(event){ window.URL.revokeObjectURL(imgUrl); } } </script> </body>
上面主要介绍 Blob URL
和 Data URL
两种方式实现图片本地预览,这两个类型的区别在**《五、拓展》**中介绍。
2. 图片本地预览 + 分片上传
实现本地预览:
将 input
获取到的 file
对象,通过实例化 FileReader
,赋值给变量 reader
,调用reader
的 readAsDataURL
方法,将 file
对象转换为 dataURL
,然后监听 reader
的 onload
属性,获取到读取结果 result
,然后设置为图片的 src
值。
实现分片上传:
由于 File 是特殊类型的 Blob,可用于任意 Blob 类型的上下文,所以针对大文件传输,我们可以使用 slice
方法进行文件切割,分片上传。
<body> <input type="file" accept="image/*" onchange="selectFile(event)"> <button onclick="upload()">上传</button> <img id="output"> <script> const chunkSize = 10000; const url = "https://httpbin.org/post"; async function selectFile(){ // 本地预览 const reader = new FileReader(); reader.onload = function(){ const output = document.querySelector("#output") output.src = reader.result; } reader.readAsDataURL(event.target.files[0]); // 分片上传 await upload(event.target.files[0]); } async function upload(files){ const file = files; for(let start = 0; start < file.size; start += chunkSize){ const chunk = file.slice(start, start + chunkSize + 1); const fd = new FormData(); fd.append("data", chunk); await fetch(url, { method: "post", body: fd }).then((res) =>{ console.log(res) res.text(); }); } } </script> </body>
3. 图片本地预览 + 分片上传 + 暂停 + 续传
<body> <input type="file" accept="image/*" onchange="selectFile(event)"> <button onclick="upload()">上传</button> <button onclick="pause()">暂停</button> <button onclick="continues()">继续</button> <img id="output" src="" alt=""> <script> const chunkSize = 30000; let start = 0, curFile, isPause = false; const url = "https://httpbin.org/post"; async function selectFile(){ const reader = new FileReader(); reader.onload = function(){ const output = document.querySelector("#output") output.src = reader.result; } reader.readAsDataURL(event.target.files[0]); curFile = event.target.files[0]; } async function upload(){ const file = curFile; for(start; start < file.size; start += chunkSize){ if(isPause) return; const chunk = file.slice(start, start + chunkSize + 1); const fd = new FormData(); fd.append("data", chunk); await fetch(url, { method: "post", body: fd }).then((res) =>{ res.text() } ); if(chunk.size < chunkSize){ uploadSuccess(); return; } } } function pause(){ isPause = true; } function continues(){ isPause = false; upload(); } function uploadSuccess(){ isPause = false; start = 0; } </script> </body>