定义
Blob
Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
ArrayBuffer
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。
它是一个字节数组,通常在其他语言中称为“byte array”。
你不能直接操作 **ArrayBuffer** 的内容,而是要通过类型数组对象或 **DataView** 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
总结:要操作二进制,首先需要把二进制数据放在一个地方,ArrayBuffer就是充当这样一个容器的作用。
区别:Blob与ArrayBuffer的区别在于,Blob是不可变的数据,而ArrayBuffer是可以编辑的。
类型化数组(TypedArray 对象)
类型
单个元素值的范围
大小(bytes)
描述
Web IDL 类型
C 语言中的等价类型
-128 to 127
1
8 位二进制有符号整数
byte
int8_t
0 to 255
1
8 位无符号整数(超出范围后从另一边界循环)
octet
uint8_t
0 to 255
1
8 位无符号整数(超出范围后为边界值)
octet
uint8_t
-32768 to 32767
2
16 位二进制有符号整数
short
int16_t
0 to 65535
2
16 位无符号整数
unsigned short
uint16_t
-2147483648 to 2147483647
4
32 位二进制有符号整数
long
int32_t
0 to 4294967295
4
32 位无符号整数
unsigned long
uint32_t
1.2×10-38 to 3.4×1038
4
32 位 IEEE 浮点数(7 位有效数字,如 1.1234567)
unrestricted float
float
5.0×10-324 to 1.8×10308
8
64 位 IEEE 浮点数(16 有效数字,如 1.123...15)
unrestricted double
double
-263 to 263-1
8
64 位二进制有符号整数
bigint
int64_t (signed long long)
0 to 264-1
8
64 位无符号整数
Int8Array是8 位二进制有符号整数,Uint8Array是8 位无符号整数,其中有符号的意思是数组中的元素可以存在符号,即可以是负数。
var ui= new Uint8Array([1, -2, 3]);
console.log(ui);//Uint8Array(3) [1, 254, 3]
var Int= new Int8Array([1, -2, 3]);
console.log(Int);//Int8Array(3) [1, -2, 3]
Uint8Array表示数组中每个元素占8位(1个字节),Uint16Array表示数组中每个元素占16位(2个字节)。
var ui8= new Uint8Array([1, -2, 3, 4]);
var ui16= new Uint16Array([1, 3]);
console.log(ui8.byteLength);//4
console.log(ui16.byteLength);//4
每一种视图都有一个BYTES_PER_ELEMENT常数,表示这种数据类型占据的字节数。刚好验证了上面。
Uint8Array.BYTES_PER_ELEMENT//1
Uint16Array.BYTES_PER_ELEMENT//2
为什么会有Blob
一直以来,JS都没有比较好的可以直接处理二进制的方法。而blob的存在,允许我们可以通过js直接操作二进制数据。
创建
生成Blob
var aBlob = new Blob( array, options );
参数
-
array
是一个由
ArrayBuffer,ArrayBufferView,Blob,DOMString等对象构成的Array,或者其他类似对象的混合体,它将会被放进Blob。DOMStrings会被编码为UTF-8。 -
options
是一个可选的
BlobPropertyBag字典,它可能会指定如下两个属性: -
type,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。 -
endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者"transparent",代表会保持blob中保存的结束符不变
生成ArrayBuffer
var arrayBuffer=new ArrayBuffer(length);
参数
length
要创建的 ArrayBuffer 的大小,单位为字节。
返回值
一个指定大小的 ArrayBuffer 对象,其内容被初始化为 0。
示例
字符串blob
var aBlob=new Blob(['春']);
console.log(aBlob);//Blob(3) {size: 3, type: ""}
var bBlob=new Blob(['a']);
console.log(bBlob);//Blob(1) {size: 1, type: ""}
从我的第一篇文章中可以知道一个中文的UTF-8占三个字节,原始的ASCll码只占用一个字节,
从上面的结果可以就可以验证这一点。
创建ArrayBuffer对象
const buffer = new ArrayBuffer(3);
console.log(buffer.byteLength);//3
判断是否是视图实例(类型数组对象或 DataView 对象)
console.log(ArrayBuffer.isView(new Int8Array()));// true
console.log(ArrayBuffer.isView(new Uint32Array(3))); //true
console.log(ArrayBuffer.isView(new Blob()));//false;
操作ArrayBuffer
类型数组对象操作
创建了一个 4字节的缓冲区,并使用一个Unit8Array(视图实例)来引用它。ArrayBuffer表示了一段内存,但是却没有规定如何分隔里面的内容。比如用Unit8Array引用就表示8位一分隔,Unit16Array表示16位一分隔。
var buffer=new ArrayBuffer(4);
console.log(buffer);//ArrayBuffer(4) {};创建一个4个字节的区域,但是里面并没有内容;
var bytes = new Uint8Array( buffer );//ArrayBuffer 转换为 TypedArray(类数组对象);
var Int=new Uint16Array(buffer);
console.log(bytes);//Uint8Array(4) [0, 0, 0, 0];给4个字节区域用Unit8Array填充内容;
console.log(Int);//Uint16Array(2) [0, 0];4个字节,所以Uint16Array只有2个因为每个Uint16Array占2个字节
这个也就是我第三篇文章中ArrayBuffer转base64操作所用的方法。
DataView操作
var buffer= new ArrayBuffer(16);
var view= new DataView(buffer);
view.setUint8(1,20);//写
view.getUint8(1);//读20
dataview.getUint8(byteOffset [, littleEndian])
参数解析:
(1).byteOffset:必需,从哪个字节开始读取数据,0表示第一个字节,以此类推。
(2).littleEndian:可选,布尔值,规定是大端字节序还是小端字节序;true表示小端字节序,省略或者false表示大端字节序。
总结:计算机默认使用小端字节序,所以使用DataView读取Unit16Array的时候要使用view.getUint16(0,true)。
区别:类数组和DataView操作的区别在于,类数组操作会有字节序问题,而DataView可以指定字节序。
类数组对象转ArrayBuffer
var bytes = new Uint8Array([1,2,3]);
var buf=bytes.buffer;
console.log(buf);//ArrayBuffer(3) {};
字节序
var bytes = new Uint8Array([1,2,2,3]);//类数组对象
var buf=bytes.buffer;//转成ArrayBuffer对象;ArrayBuffer(4) {};
var ui8 = new Uint8Array(buf);
console.log(ui8);//Uint8Array(4) [1, 2, 2, 3];
var ui16 = new Uint16Array(buf);
console.log(ui16);//Uint16Array(2) [513, 770])
var view=new DataView(buf);
console.log(view.getUint8(0));//1
console.log(view.getUint16(0,true));//513
1, 2, 2,3 (十进制)
00000001, 00000010, 00000010,00000011 (二进制)
513,770(十进制)
1000000001,1100000010(二进制)
上面可以看出513这个 2 字节的数据,其实是把 1 和 2 这两个挨着的 1 字节的数据,以 倒序 方式拼接在一起的。也就是字节序。
相互转换
Blob转ArrayBuffer
//第一种方法
var blob=new Blob(['春节']),fileReader=new FileReader();
fileReader.onload=function(e){
console.log(this.result);//ArrayBuffer(6) {}
}
fileReader.readAsArrayBuffer(blob);
//第二种方法(有兼容性问题)
var blob=new Blob(['春节']);
blob.arrayBuffer().then(res=>{console.log(res)})
ArrayBuffer转Blob
var blob=new Blob(['春节']),fileReader=new FileReader(),buffer;
fileReader.onload=function(e){
console.log(this.result);//ArrayBuffer(6) {}
buffer=this.result;
var newBlob=new Blob([buffer]);
newBlob.text().then(res=>{console.log(res)})//春节
}
fileReader.readAsArrayBuffer(blob);
Blob应用
图片显示
1.Data URL:表示base64 编码的图像数据。
可以看我上一篇文章有blob转base64显示的示例。
2.HTTP URL:表示从服务器上获取图片。
//掘金收藏集图片
<img data-v-391f1edb="" src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/img/collections.945b9ae.png~tplv-t2oaga2asx-image.image" class="icon">
3.Object URL:用来代表存储在浏览器内存中的 File 或 Blob 对象。Object URL 可以由createObjectURL API 来创建,并由 revokeObjectURL API 释放。
// 掘金首页收藏集图片
fetch('https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/img/collections.945b9ae.png~tplv-t2oaga2asx-image.image',{
method: 'get',
responseType: 'blob'
}).then(res =>res.blob()).then(res=> {
var herf=URL.createObjectURL(res);
console.log(href);//blob:https://juejin.im/c6322385-2ed5-47de-8dd4-9faa5acd4eb4
//替换相应的url也能够显示图片
URL.revokeObjectURL(herf);//释放
})
//<img data-v-391f1edb="" src="blob:https://juejin.im/c6322385-2ed5-47de-8dd4-9faa5acd4eb4" class="icon">
图片压缩
-
获取上传 Input 中的图片对象 File
-
图片通过 Canvas 转换压缩,这里会用到的 Canvas 的 drawImage 以及 toDataURL 这两个 Api,一个调节图片的分辨率的,一个是调节图片压缩质量。
//掘金首页收藏集图片;//为了演示,用获取的blob代替从input对象获取的File对象 var image = new Image(); //新建一个img标签; fetch('p1-jj.byteimg.com/tos-cn-i-t2…', { method: 'get', responseType: 'blob' }).then(res => res.blob()).then(res => { console.log(res);//Blob(833) {size: 833, type: "image/png"} var href = URL.createObjectURL(res); image.src = href; image.onload = function() { compress(image, 'png', 92,100); //canvas压缩百分之92;图片保存原有大小 }; }) function compress(image, type, quality,targetPercent) { let canvas = document.createElement('canvas'); let context = canvas.getContext('2d'); // 定义 canvas 大小,也就是压缩后下载的图片大小 let imageWidth = image.width; //压缩后图片的大小 let imageHeight = image.height; canvas.width = imageWidthtargetPercent/100; canvas.height = imageHeighttargetPercent/100; context.drawImage(image, 0, 0,canvas.width,canvas.height); var compressImg = canvas.toDataURL(
image/${type}, quality / 100); console.log(compressImg); //base64数据; canvas.toBlob(function(blob) {//canvas转blob; console.log(blob);//Blob(725) {size: 725, type: "image/png"} }) }
toDataURL() 方法,该方法接收 type 和 encoderOptions 两个可选参数。
其中 type 表示图片格式,默认为 image/png。而 encoderOptions 用于表示图片的质量,在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。
图片压缩到指定大小
fetch('https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/img/collections.945b9ae.png~tplv-t2oaga2asx-image.image', {
method: 'get',
responseType: 'blob'
}).then(res => res.blob()).then(res => {
console.log(res.size);//Blob(833) {size: 833, type: "image/png"}
var image = new Image();
var href = URL.createObjectURL(res);
image.src = href; image.onload = function() {
compress(image,res,1); //将流压缩到3M以内 };
})
//base64转blob;
function dataURLtoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: mimeString});
}
function compress(image,blob,size) {
//blob原始的流 size要压缩的流的大小
let oldSize=blob.size;
if(oldSize>size*1024*1024){//大于1M才压缩
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
// 定义 canvas 大小,也就是压缩后下载的图片大小
let imageWidth = image.width; //原始图片的大小
let imageHeight = image.height;
let targetPercent=(size*1024*1024/oldSize).toFixed(2);
canvas.width = imageWidth*targetPercent;
canvas.height = imageHeight*targetPercent;
context.drawImage(image, 0, 0,canvas.width,canvas.height);
var compressImg = canvas.toDataURL(blob.type,1);
return dataURLtoBlob(compressImg);
}else{
return blob;
}
}
文件
分片上传
const file = new File(["a".repeat(1000000)], "test.txt");
const chunkSize = 40000;
const url = "https://httpbin.org/post";
async function chunkedUpload() {
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) =>
res.text()
);
}
}
文件下载
var url = 'https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/img/collections.945b9ae.png~tplv-t2oaga2asx-image.image'; //文件下载地址fetch(url, { method: 'get', responseType: 'blob'}).then(res => res.blob()).then(res => { var url = window.URL.createObjectURL(res); var a = document.createElement("a"); a.href = url; a.download = "测试"; a.click(); a.remove(); window.URL.revokeObjectURL(url);})