什么是blob?

以下是MDN的官方解释:

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。 
Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
要从其他非blob对象和数据构造一个 Blob,请使用 Blob() 构造函数。

实际上 file 对象只是 blob 对象的一个更具体的版本,blob 存储着大量的二进制数据,并且 blobsizetype 属性,都会被 file 对象所继承。
所以, 在大多数情况下,blob 对象和 file 对象可以用在同一个地方,例如,可以使用 FileReader 借口从 blob 读取数据,也可以使用 URL.createObjectURL()blob 创建一个新的 URL 对象。

blob的创建

Blob() 构造函数允许通过其它对象创建 Blob 对象。比如,用字符串构建一个 blob

1
2
var debug = {hello: "world"};
var blob = new Blob([JSON.stringify(debug, null, 2)], {type : 'application/json'});

构造函数,接受两个参数,第一个为一个数据序列,可以是任意格式的值,例如,任意数量的字符串,Blobs 以及 ArrayBuffers。第二个参数,是一个包含了两个属性的对象,其两个属性分别是:

type – MIME 的类型。

endings – 决定 append() 的数据格式,(数据中的 \n 如何被转换)可以取值为 “transparent” 或者 “native”(t* 的话不变,n* 的话按操作系统转换;t* 为默认) 。

blob的应用

大文件分割(分片上传)
slice() 方法接受三个参数,起始偏移量,结束偏移量,还有可选的 mime 类型。如果 mime 类型,没有设置,那么新的 Blob 对象的 mime 类型和父级一样。

当要上传大文件的时候,此方法非常有用,可以将大文件分割分段,然后各自上传,因为分割之后的 Blob 对象和原始的是独立存在的。

不过目前浏览器实现此方法还没有统一,火狐使用的是 mozSlice() ,Chrome 使用的是 webkitSlice() ,其他浏览器则正常的方式 slice()

可以写一个兼容各浏览器的方法:

1
2
3
4
5
6
7
8
9
10
function sliceBlob(blob, start, end, type) {
type = type || blob.type;
if (blob.mozSlice) {
return blob.mozSlice(start, end, type);
} else if (blob.webkitSlice) {
return blob.webkitSlice(start, end type);
} else {
throw new Error("This doesn't work!");
}
}

文件流下载

后端返回的数据是文件流的时候,需要在axios请求指定responseType:

1
2
3
4
5
6
7
8
9
10
// 下载接口 文件下载
export function downloadRes(data) {
return request({
timeout: 30000,
responseType: 'blob', // 需要指定
baseURL: 'xxx',
method: 'post',
data
})
}

这是返回的data就是blob类型数据,但是我们下载的话还需要一个文件名,后端会默认指定把文件名放在content-disposition这个响应头:

1
2
3
4
5
let fileName = '';
const contentDisposition = res.headers['content-disposition'];
if (contentDisposition) {
fileName = window.decodeURI(res.headers['content-disposition'].split('=')[1], "UTF-8");
}

创建blob对象,创建reader,监听加载时间,创建a标签,模拟点击事件,移除a标签。这就是一个文件流下载的基本流程:

1
2
3
4
5
6
7
8
9
10
11
const blob = new Blob([res.data]);
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = (e) => {
const a = document.createElement('a');
a.download = fileName;
a.href = e.target.result;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}