前言
上传与下载是每个前端必定会遇到的需求场景,上传我们常用封装好的UI组件,例如Antd的Upload组件,Element的el-upload组件,下载使用同事封装好的hook或者file-saver包。这些通常能对付大部分情况,但我们不能只会用,否则遇到特定需求或新增功能时,会非常的麻烦。
让我们来了解上传与下载相关的:Blob、File与ArrayBuffer
一、概述
Blob:专门用于支持文件操作的二进制对象
File:继承Blob,并额外包含文件的元数据,如文件名和修改日期
ArrayBuffer:通用二进制缓冲区,类似数组,但长度固定,不可以增加和删除
关系图:

二、Blob基本使用
1. 创建Blob对象
可以使用 new Blob() 构造函数来创建一个 Blob 对象
js
new Blob(blobParts)
new Blob(blobParts, options)
blobParts:一个可迭代对象,比如 Array,包含 ArrayBuffer、TypedArray、DataView、Blob、字符串或者任意这些元素的混合,这些元素将会被放入 Blob 中。
options:可选参数,表示媒体类型。媒体类型列表参照
js
new Blob(["Hello World"], { type: "text/plain" })

从打印的结果来看,Blob对象有2个实例属性,4个实例方法
2. 实例属性
-
Blob.size
:Blob 对象中所包含数据的大小(字节) -
Blob.type
:表明该 Blob 对象所包含数据的媒体类型
3. 实例方法
Blob.arrayBuffer
:返回一个 promise,将Blob转为ArrayBuffer对象
Blob是一个文件对象,缺乏对二进制数据的细节操作能力,比如要具体修改某一部分的二进制数据,则需先转换为ArrayBuffer。例如对于图像或音频文件,有时候需要直接操作像素值或音频样本,这时就需要将 Blob 转换成 ArrayBuffer,再进一步解析成更具体的格式
js
const blob = new Blob(["Hello World"], { type: "text/plain" })
blob.arrayBuffer().then((buffer) => {
console.log(buffer);
});
Blob.slice
:返回一个新的 Blob 对象,类似于数组的slice方法,可以提取指定范围内的数据。常用于大文件分段上传。
js
const part1 = blob.slice(0, 5);
Blob.stream
:返回一个可读流(ReadableStream),用于逐步读取Blob内容。常用于大文件分段读取。
js
async function processBlob(blob) {
const reader = blob.stream().getReader();
const decoder = new TextDecoder('utf-8');
let done, value;
while (!done) {
({ value, done } = await reader.read());
if (value) {
// 将 Uint8Array 转换为字符串
console.log(decoder.decode(value, { stream: true }));
}
}
console.log("完成读取");
}
// 创建一个包含文本的 Blob
const myBlob = new Blob(['Hello, world!'], { type: 'text/plain' });
// 处理 Blob
processBlob(myBlob);
Blob.text
:返回一个 Promise,解析为文本数据
js
blob.text().then((text) => {
console.log(text);
});
三、Blob对象获取方式
共有五种常用方式获取Blob对象
1. 使用构造函数创建
js
new Blob(["Hello World"])
2. 使用 fetch 获取
js
fetch("https://wen.caowj.top/static/wx.jpg")
.then((res) => res.blob())
.then((blob) => {
console.log(blob);
});
3. 使用 XMLHttpRequest 获取
js
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://wen.caowj.top/static/wx.jpg", true);
xhr.responseType = "blob";
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.response);
}
};
xhr.onerror = function () {};
xhr.send();
4. 从 Canvas 中获取
canvas提供了canvas.toBlob() 方法将画布内容转换为 Blob 对象
js
const canvas = document.getElementById('myCanvas');
canvas.toBlob(function(blob) {
console.log(blob);
}, 'image/png');
5. 从文件输入中获取
从文件输入中获取File对象共有2种方式:1. 标签上所选择的文件;2. 拖拽中生成的DataTransfer对象。
每个 File 对象都是 Blob 的子类,Blob上的实例属性和方法在File上都可以使用。这里以input为例。
js
<input type="file" id="fileInput">
<script>
document.getElementById('fileInput').addEventListener('change', function(event) {
const files = event.target.files;
if (files.length > 0) {
const file = files[0];
console.log(file);
}
});
</script>
四、Blob能够实现的功能
1. 图片显示
通过window.URL.createObjectURL(blob)生成Blob URL,赋给img.src属性
js
<body>
<input type="file" id="inputId" />
<img id="img" style="width: 200px; height: 200px" />
<script>
document.getElementById("inputId").addEventListener(
"change",
function (e) {
var file = this.files[0];
const img = document.getElementById("img");
const url = window.URL.createObjectURL(file);
img.src = url;
img.onload = function () {
// 释放一个之前通过调用 URL.createObjectURL创建的 URL 对象
window.URL.revokeObjectURL(url);
};
},
false
);
</script>
</body>
Blob URL只是类似于浏览器内部的引用,实际的数据通常会保存在浏览器的内存中
2. 文件下载
通过window.URL.createObjectURL(blob)生成Blob URL,赋值给a.download属性。
js
<script>
const blob = new Blob(["Hello World"]);
const url = URL.createObjectURL(blob); // 创建一个 Blob URL
const a = document.createElement("a");
a.href = url;
a.download = "Hello.txt";
a.click();
URL.revokeObjectURL(url); // 释放URL对象,避免内存泄漏
</script>
提示:download属性不兼容IE, 对IE可通过window.navigator.msSaveBlob方法
3. 文件上传
文件上传常见的使用场景就是更换用户头像,更换头像不涉及大文件上传,因此这里不展示切片上传,而是直接上传。大文件上传之后再另开一篇介绍。
js
<body>
<input type="file" id="inputId" />
<img id="img" style="width: 200px; height: 200px" />
<script>
document.getElementById("inputId").addEventListener(
"change",
function (e) {
//获取blob对象
const blob = this.files[0];
const formData = new FormData();
//将 File 对象传入到 formData 中上传,对应的属性为file
formData.append("file", blob);
fetch("http://127.0.0.1:9000/api/upload", {
method: "POST",
body: formData,
}).then((response) => {
console.log("File uploaded successfully");
});
},
false
);
</script>
</body>
后端部分可参考我的另一篇文章:Express文件上传
4. 本地读取文件
Blob除了通过window.URL.createObjectURL转为Blob URL对文件进行下载或图片显示,还可以借助FileReader转为其他数据类型:
-
FileReader.readAsText(Blob):将Blob转化为文本字符串
-
FileReader.readAsArrayBuffer(Blob): 将Blob转为ArrayBuffer格式数据
-
FileReader.readAsDataURL(): 将Blob转化为Base64格式的Data URL
例:将Blob转为Base64格式的Data URL,显示图片
js
<body>
<input type="file" id="inputId" />
<img id="img" style="width: 200px; height: 200px" />
<script>
document.getElementById("inputId").addEventListener(
"change",
function (e) {
var file = this.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
const url = reader.result;
const img = document.getElementById("img");
img.src = url;
};
},
false
);
</script>
</body>
五、ArrayBuffer
ArrayBuffer 对象用来表示通用的原始二进制数据缓冲区。
1. 与原生数组区别
(1)ArrayBuffer初始化后固定大小
(2)数组放在堆中,ArrayBuffer把数据放在栈中
(3)ArrayBuffer没有push、pop等数组的方法
(4)ArrayBuffer只能读不能写,写要借助TypeArray或者DataView
2. ArrayBuffer对象获取方式
1. 通过Blob直接转换
js
const blob = new Blob(["Hello World"], { type: "text/plain" });
blob.arrayBuffer().then((buffer) => {
console.log(buffer);
});

2. 通过FileReader.readAsArrayBuffer转换
js
<body>
<input type="file" id="inputId" />
<script>
document.getElementById("inputId").addEventListener(
"change",
function (e) {
var file = this.files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {
const result = reader.result;
console.log(result)
};
},
false
);
</script>
</body>
3. 使用 fetch 获取
js
fetch("https://wen.caowj.top/static/wx.jpg")
.then((res) => res.arrayBuffer())
.then((arrayBuffer) => {
console.log(arrayBuffer);
});
4. 使用 XMLHttpRequest 获取
js
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://wen.caowj.top/static/wx.jpg", true);
xhr.responseType = "arraybuffer";
xhr.onload = function () {
console.log(xhr.response)
}
xhr.send();
3. TypeArray
TypedArray是一个描述底层二进制数据缓冲区的类数组视图,它是一个泛称,可具体到以下这些:Int8Array、Uint8Array、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array 和 Float64Array。每种类型都有各自的使用场景。
这里以Uint8Array为例,Uint8Array 用于存储 8 位无符号整数。它只能表示从 0 到 255 的非负整数值。适合处理文本编码、图片像素等。
- 基本使用
js
// 直接创建一个 TypedArray,会自动创建一个 ArrayBuffer
const uint8 = new Uint8Array(2);
uint8[0] = 255; // 最大值
uint8[1] = 0; // 最小值
console.log(uint8); // 输出: Uint8Array [255, 0]
- 示例:对两段文本进行拼接,模拟分段下载的场景
js
<script>
function getArrayBuffer(text) {
const blob = new Blob([text], { type: "text/plain" });
return blob.arrayBuffer();
}
//模拟网络请求1
const p1 = getArrayBuffer("Hello World");
//模拟网络请求2
const p2 = getArrayBuffer("jack");
Promise.all([p1, p2]).then((res) => {
const [buffer1, buffer2] = res;
const arr = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
//通过 set 方法将两个 ArrayBuffer 的内容复制到新数组中
arr.set(new Uint8Array(buffer1), 0);
arr.set(new Uint8Array(buffer2), buffer1.byteLength);
const blob = new Blob([arr.buffer]);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "Hello.txt";
a.click();
URL.revokeObjectURL(url);
});
</script>
4. DataView
DataView 是另一个视图,相比于TypeArray手动挑选类型,它允许你在同一个缓冲区内混合使用多种数据类型
js
<script>
// 创建一个 16 字节的 ArrayBuffer
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt8(2, 42);
console.log(view.getInt8(2));
// 输出: 42
</script>
总结
参考文章: