ArrayBuffer
ArrayBuffer
对象代表储存二进制数据的一段内存,它不能直接读写,只能通过视图进行操作。
// 创建一个长度为 16 的 buffer 它会分配一个 16 字节(byte)的连续内存空间,并用 0 进行预填充。
const buffer1 = new ArrayBuffer(16);
对于位、字节、字不太理解的可以先看文章末尾扩展部分的介绍。
TypedArray
TypedArray
是一组构造函数,一共包含九种类型,每一种都是一个构造函数。
TypedArray的构造函数接受三个参数,第一个ArrayBuffer(其实还可以是数组、视图这里不细说)对象,第二个视图开始的字节号(默认0),第三个视图结束的字节号(默认直到本段内存区域结束)。
名称 | 占用字节 | 描述 |
---|---|---|
Int8Array | 1 | 8位有符号整数 |
Uint8Array | 1 | 8位无符号整数 |
Uint8ClampedArray | 1 | 8位无符号整型固定数组(数值在0~255之间) |
Int16Array | 2 | 16位有符号整数 |
Uint16Array | 2 | 16位无符号整数 |
Int32Array | 4 | 32 位有符号整数 |
Uint32Array | 4 | 32 位无符号整数 |
Float32Array | 4 | 32 位 IEEE 浮点数 |
Float64Array | 8 | 64 位 IEEE 浮点数 |
// Uint8Array —— 将 ArrayBuffer 中的每个字节(8位)视为 0 到 255 之间的单个数字(每个字节是 8 位)。这称为 “8 位无符号整数”。
// Uint16Array —— 将每 2 个字节(16位)视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。
// Uint32Array —— 将每 4 个字节(32位)视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。
// Float64Array —— 将每 8 个字节(64位)视为一个 5.0x10-324 到 1.8x10308 之间的浮点数。
const uint8 = new Uint8Array(buffer1);
const uint16 = new Uint16Array(buffer1);
const uint32 = new Uint32Array(buffer1);
const float64 = new Float64Array(buffer1);
我们可以对其进行操作
console.log(uint8);
uint8[0] = 255;
console.log(uint8);
前后对比发现第0位被我们设置值了
如果我想再想获取到ArrayBuffer
怎么办呢?
可以通过 obj.buffer
获取,比如上面的例子我们就可以使用uint8.buffer
获取到原始ArrayBuffer
。
DataView
DataView
就是一种更灵活的视图,DataView视图支持除Uint8ClampedArray以外的八种类型。DataView比使用TypedArray更方便,只需要简单的创建一次就能进行各种转换。
// 可以转成各种格式
const dataView1 = new DataView(buffer1);
// 接受三个参数,1.字节序号,2.写入的数据,3.写入方式(true:小端/false|undeifined:大端)
dataView1.setFloat64(0, 25, false); // 在第一个字节以大端字节序写入一个值为25的32位整数
console.log(dataView1);
console.log(dataView1.getUint8(0)); // 64
console.log(dataView1.getUint16(0)); // 16441
console.log(dataView1.getUint32(0)); // 1077477376
console.log(dataView1.getFloat64(0)); // 25
如果我想再想获取到ArrayBuffer
怎么办呢?
可以通过 obj.buffer
获取,比如上面的例子我们就可以使用dataView1.buffer
获取到原始ArrayBuffer
。
Blob
Blob
对象表示一个不可变、原始数据的类文件对象。
// 构造函数
const blob = new Blob(array, options)
-
array 是一个由
ArrayBuffer, ArrayBufferView, Blob, DOMString
等对象构成的数组,DOMStrings
会被编码为UTF-8。 -
options 是一个可选,它可能会指定如下两个属性:
- type,默认值为
""
,内容的MIME类型。 - endings,默认值为
"transparent"
,用于指定包含行结束符\n
的字符串如何被写入。 它是以下两个值中的一个:"native"
,代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者"transparent"
,代表会保持blob中保存的结束符不变
- type,默认值为
const blob1 = new Blob(['hello randy'], { type: "text/plain" });
实例属性、方法
属性
type
类型 常见的MIME 类型size
大小、单位为字节
const blob = new Blob(["hello", "randy"], { type: "text/plain" });
// 输出的对象有如下属性
// size: 10;
// type: "text/plain";
console.log(blob);
方法
slice()
从Blob
中截取一部分并返回一个新的Blob
(用法同数组的slice
)arrayBuffer()
返回一个以二进制形式展现的promise
stream()
返回一个ReadableStream
对象text()
返回一个文本形式的promise
// 转成stream
console.log(blob.stream());
// 转成Arraybuffer
blob.arrayBuffer().then((res) => {
console.log(res);
});
// 转成文本
blob.text().then((res) => {
console.log(res);
});
blob url
简单的理解一下就是将一个file
或Blob
类型的对象转为UTF-16
的字符串,并保存在当前操作的document
下,存储在内存中。
类似这样一个链接
blob:http://localhost:3000/53acc2b6-f47b-450f-a390-bf0665e04e59
生成blob url使用的方法是URL.createObjectURL(file/blob)
。清除方式只有页面unload()
事件或者使用URL.revokeObjectURL(objectURL)
手动清除 。
这在前端下载中经常会用到。
export const downloadFile = async (params, fileName) => {
// 我们使用axios设置接口返回类型 responseType: "blob", 所以这里从后端返回的是blob。
const results = await download(params);
const a = document.createElement("a");
a.download = fileName + ".xlsx";
// 生成blob url。这里可以使用Blob对象或者File对象
a.href = window.URL.createObjectURL(results);
a.style.display = "none";
document.body.appendChild(a);
a.click();
// 释放内存
window.URL.revokeObjectURL(a.href);
document.body.removeChild(a);
};
File
File
描述文件信息的一个对象,可以让 JavaScript
访问文件信息。File
继承于 Blob
。
const file = new File(array, name[, options])
-
array 是一个由
ArrayBuffer, ArrayBufferView, Blob, DOMString
等对象构成,DOMStrings
会被编码为UTF-8。 -
name 表示文件名称,或者文件路径。
-
options 是一个可选,它可能会指定如下两个属性:
- type,默认值为
""
,内容的MIME类型。 - lastModified: 数值,表示文件最后修改时间的 Unix 时间戳(毫秒)。默认值为 Date.now()。
- type,默认值为
实例属性、方法
属性
type
类型 常见的MIME 类型size
大小、单位为字节name
文件名称lastModified
最后修改时间(时间戳)lastModifiedDate
最后修改时间
const file1 = new File(["文件对象"], "test", { type: "text/plain" });
// 输出的对象有如下属性
// lastModified: 1640589621358
// lastModifiedDate: Mon Dec 27 2021 15:20:21 GMT+0800 (中国标准时间) {}
// name: "test"
// size: 12
// type: "text/plain"
// webkitRelativePath: ""
console.log(file1);
方法
slice()
从Blob
中截取一部分并返回一个新的Blob
(用法同数组的slice
)arrayBuffer()
返回一个以二进制形式展现的promise
stream()
返回一个ReadableStream
对象text()
返回一个文本形式的promise
// 转成stream
console.log(file1.stream());
// 转成Arraybuffer
file1.arrayBuffer().then((res) => {
console.log(res);
});
// 转成文本
file1.text().then((res) => {
console.log(res);
});
Base64
定义
Base64是一种编码格式,在前端经常会碰到,格式是 data:[<mediatype>][;base64],<data>
。
除了使用工具进行Base64编码外,js还内置了两个方法能进行字符串的Base64的编码和解码。
const str1 = "hello randy";
// 编码
const b1 = window.btoa(str1);
console.log(b1); // aGVsbG8gcmFuZHk=
// 解码
const str2 = window.atob(b1);
console.log(str2); // hello randy
优点
- 可以将二进制数据(比如图片)转化为可打印字符,方便传输数据。
- 对数据进行简单的加密,肉眼是安全的。
- 如果是在html或者css处理图片,可以减少http请求。
缺点
- 内容编码后体积变大, 至少大1/3。因为是三字节变成四个字节,当只有一个字节的时候,也至少会变成三个字节。
- 编码和解码需要额外工作量。
FileReader
FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容.
属性
属性 | 描述 |
---|---|
FileReader.error | 一个DOMException,表示在读取文件时发生的错误 。 |
FileReader.result | 返回文件的内容。只有在读取操作完成后,此属性才有效,返回的数据的格式取决于是使用哪种读取方法来执行读取操作的。 |
FileReader.readyState | 表示FileReader状态的数字。0 还没有加载任何数据。1 数据正在被加载。2 已完成全部的读取请求。 |
方法
需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result属性中。
方法名 | 描述 |
---|---|
FileReader.abort() | 中止读取操作。在返回时,readyState 属性为 DONE。 |
FileReader.readAsArrayBuffer() | 将读取的内容转成ArrayBuffer。 |
FileReader.readAsBinaryString() | 将读取的内容转成二进制数据。 |
FileReader.readAsDataURL() | 将读取的内容转成并将其编码为 base64 的 data url。 格式是 data:[<mediatype>][;base64],<data> |
FileReader.readAsText() | 将数据读取为给定编码(默认为 utf-8 编码)的文本字符串。 |
事件
事件 | 描述 |
---|---|
FileReader.onabort | 处理 abort 事件。该事件在读取操作被中断时触发。 |
FileReader.onerror | 处理 error 事件。该事件在读取操作发生错误时触发。 |
FileReader.onload | 处理 load 事件。该事件在读取操作完成时触发。 |
FileReader.onloadstart | 处理 loadstart 事件。该事件在读取操作开始时触发。 |
FileReader.onloadend | 处理 loadend 事件。该事件在读取操作结束时(要么成功,要么失败)触发。 |
FileReader.onprogress | 处理 progress 事件。该事件在读取Blob时触发。 |
例子
const blob3 = new Blob(["hello", "randy"], { type: "text/plain" });
const fileReader = new FileReader();
fileReader.readAsDataURL(blob3);
fileReader.onload = () => {
console.log(fileReader);
// 通过fileReader获取结果
// fileReader.result 是结果(如果成功)
// fileReader.error 是 error(如果失败)。
};
相互转换
Blob和File的互相转换
Blob转File
const blob1 = new Blob(["blob文件"], { type: "text/plain" });
// blob转file
const file2 = new File([blob1], "test2", { type: blob1.type });
console.log("file2: ", file2);
File转Blob
const file1 = new File(["文件对象"], "test", { type: "text/plain" });
// file转blob
const blob2 = new Blob([file1], { type: file1.type });
console.log("blob2: ", blob2);
File、Blob、img转Base64
Blob转Base64
// Blob转Base64
const blob = new Blob(["hello", "randy"], { type: "text/plain" });
const fileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onload = () => {
console.log(fileReader.result); // "data:text/plain;base64,aGVsbG9yYW5keQ=="
};
File转Base64
// File转Base64
const file1 = new File(["文件对象"], "test", { type: "text/plain" });
const fileReader = new FileReader();
fileReader.readAsDataURL(file1);
fileReader.onload = () => {
console.log(fileReader.result); // "data:text/plain;base64,5paH5Lu25a+56LGh"
};
img转Base64
// 本地图片转base64,注意链接是本地链接不能是网络地址。
const img2base64 = (imgUrl) => {
let image = new Image();
image.src = imgUrl;
return new Promise((resolve) => {
image.onload = () => {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let dataUrl = canvas.toDataURL("image/png");
resolve(dataUrl);
};
});
};
img2base64("../vue2/src/assets/logo.png").then((res) => {
console.log(res);
});
Base64转Blob、File
Base64转Blob
function dataURLtoBlob(dataurl) {
// `data:[<mediatype>][;base64],<data>`
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
Base64转File
function dataURLtoFile(dataurl, filename) {
//将base64转换为文件
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
扩展
位、字节、字
位(bit)、字节(byte)、字(word)是计算机数据存储的单位。位是最小的存储单位,每一个位存储一个1位的二进制码,一个字节由8位组成。而字通常为16、32或64个位组成。
位是最基本的概念,在计算机中,由于只有逻辑0和逻辑1的存在,因此很多东西、动作、数字都要表示为一串二进制的字码例如: 1001 0000 1101等等。其中每一个逻辑0或者1便是一个位。例如这个例子里的1000 1110共有八个位,它的英文名字叫(bit),是计算机中最基本的单位。
字节,是由八个位组成的一个单元,也就是8个bit组成1个Byte。字节有什么用呢? 在计算机科学中,用于表示ASCII字符,便是运用字节来记录表示字母和一些符号~例如字符A便用 “0100 0001”来表示。
字 代表计算机处理指令或数据的二进制数位数,是计算机进行数据存储和数据处理的运算的单位。对于32位计算机与64位计算机,字的大小往往不同。32位计算机:1字=32位=4字节,64位计算机:1字=64位=8字节
所以1字节就是8位。1字有可能是2字节、4字节、8字节。
字符集、编码
字符集
即是文字符号和二进制的一种映射关系。因为电脑只认识二进制,所以就需要把我们平时使用的字符来用二进制表示并存储到电脑。常用的字符集有ASCII、unicode、GB2312/GBK
比如在ASCII
字符集中,大写字母A
对应的ASCII码是65
,二进制就是01000001
;阿拉伯数字0
对应的ASCII码是48
,二进制就是110000
;还有空格32
、回车13
等等一些键盘上所见的符号。
由于ASCII
是美国信息交换标准代码
,ASCII
起初只规定了128个字符 (0-9的数字,A-Z大写和小写英文字母,以及一些特殊字符。),就能代表了所有键盘上的普通符号、阿拉伯数字和英文字母。对于英文国家来说能满足日常使用,所以他们把每个字节的第一位置置0,只需要使用后面7位就足够了。(也就是只需要使用7位,2的7次方,能表示128个不同字符)。
但是其他欧洲国家比如法语、葡萄牙语等无法用128个字符表示完全,所以他们就把第一个空闲0使用了起来。因此欧洲国家用一个字节(8位可表示256个字符)也能实现。所以ASCII
有128和256两种版本。
随着计算机在全球的发展和普及开来,其他语言,比如中文、日文,单字节256个字符肯定没法完全表示。所以就出现了一个全球统一的编码规则叫unicode
,它的作用是把各个语言的字符映射为二进制和ASCII的目的一样。
由于unicode
编码都是两字节的,偏僻的可能用了四字节。也就是说至少会用到16位的内存空间,所以如果我们需要编码的文本都是英文的,那么用unicode是不是很浪费,明明一个字节就可以,却用了两字节。因此就出现了后面的编码。
常见的编码有UFT-8、UTF-16、UTF-32
。我们来说说平时使用的最多的UTF-8
,UTF-8
编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间。
所以总结就是:
ASCII、unicode、GB2312/GBK
都是是字符集,定义每个字符对应的数字。UTF-8、UTF-16
等是unicode
字符集的编码格式,定义“字符对应的数字”如何以二进制的方式存储。
encodeURI、encodeURIComponent、escape
介绍完字符集以及编码,我们再来说说js中对字符串进行编码的三个方法encodeURI、encodeURIComponent、escape
。
escape
该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。
其他所有的字符都会被转义序列替换。
// 编码
escape('编码randy123*@-_+./') // %u7F16%u7801randy123*@-_+./
// 解码
unescape('%u7F16%u7801randy123*@-_+./') // 编码randy123*@-_+./
注意: escape() 函数已经从 Web 标准中删除,所以尽量不使用该函数,可以使用 encodeURI
或 encodeURIComponent
代替。
encodeURI
和 encodeURIComponent
这两种方法都是对URL
进行编码的,唯一的区别是编码的范围不一样。
encodeURI
方法不会对下列字符编码 :ASCII字母、数字、~!@#$&*()=:/,;?+'
。
encodeURIComponent
方法不会对下列字符编码: ASCII字母、数字、~!*()'
。
// 编码
encodeURI("编码randy~!@#$&*()=:/,;?+'") // %E7%BC%96%E7%A0%81randy~!@#$&*()=:/,;?+'
// 解码
decodeURI("%E7%BC%96%E7%A0%81randy~!@#$&*()=:/,;?+'") // 编码randy~!@#$&*()=:/,;?+'
// 编码
encodeURIComponent("编码randy~!*()'"); // %E7%BC%96%E7%A0%81randy~!*()'
// 解码
decodeURIComponent("%E7%BC%96%E7%A0%81randy~!*()'"); // 编码randy~!*()'
由此可见,encodeURIComponent
编码范围比 encodeURI
的大。
参考文章
Blob、ArrayBuffer、File、FileReader和FormData的区别
初探 Js 中的 Blob、ArrayBuffer、File 、FileRender、FormData
后记
本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个赞~~