一、基本介绍
1.ArrayBuffer
ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区。
它是一个字节数组,通常在其他语言中称为“byte array”。
你不能直接操作 ArrayBuffer
的内容,而是要通过 TypedArray
(类型数组)对象或 DataView
对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
它的含义类似 NodeJs 中的
Buffer
。简单来说,我们可以通过ArrayBuffer
来开辟一段二进制数据空间,但是它只能通过TypedArray
或者DataView
来进行操作。
简单用法:
//创建一个长度为 8 的 ArrayBuffer ,此时开辟一个固定 8 个字节的缓冲区也就是 64 位
const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength); // 8
length 大于 Number.MAX_SAFE_INTEGER(>= 2 \*\* 53)
或为负数,则抛出一个 RangeError
异常。
作用:
从 XHR、File API、Canvas, WebGL 等等各种地方,读取了一大串字节流,如果用 JS 里的 Array 去存,又浪费,又低效,
于是为了配合这些新的 API 增强 JS 的二进制处理能力,就有了 ArrayBuffer
。
创建 ArrayBuffer
的时候,就相当于申请了一块内存, 不能(也不方便)直接用它,
所以也就有了 TypedArray
,比如 Uint32Array, Int16Array, Int8Array, Float32Array
等等。
1.1.TypedArray
一个类型化数组(TypedArray)对象描述了一个底层的二进制数据缓冲区(binary data buffer)的一个类数组视图(view)。
稍微翻译下上边的话,也就是说可以通过
TypedArray
来操作ArrayBuffer
的实例。
事实上,没有名为 TypedArray
的全局属性,也没有一个名为 TypedArray
的构造函数。相反,有许多不同的全局属性,它们的值是特定元素类型的类型化数组构造函数
这句话简单来讲,你可以将
TypedArray
理解为一种接口的形式。所谓TypedArray
它并不包含具体的实现而是代表一系列具有相同特性(类数组视图)的集合概念。也就是说
TypedArray
不可被直接实例化,本身也无法访问。但是它有很多种不同的实现方式。
特点:
TypedArray
内的成员只能是同一类型,都是数字,Array 可以存储任意元素TypedArray
成员是连续的,不会有空位,Array 可以有空位TypedArray
成员的默认值为 0,Array 的默认值为空TypedArray
定义时长度固定不可动态增加或减小,Array 长度可变TypedArray
只是视图,本身不存储数据,数据都存储在底层的ArrayBuffer
中,要获取底层对象必须使用 buffer 属性TypedArray
Array.isArray()
返回 falseTypedArray
可以直接操作内存,不需要进行类型转换,所以比数组快
创建构造函数:
// 下面代码是语法格式,不能直接运行,
// TypedArray 关键字需要替换为底部列出的构造函数。
new TypedArray(); // ES2017中新增
new TypedArray(length);
new TypedArray(typedArray);
new TypedArray(object);
new TypedArray(buffer [, byteOffset [, length]]);
TypedArray
指的是以下的其中之一:
类型 | 值范围 | 大小(bytes) | 描述 | Web IDL 类型 | 等价的 C 类型 |
---|---|---|---|---|---|
Int8Array | -128 到 127 | 1 | 8 位有符号整型(补码) | byte | int8_t |
Uint8Array | 0 到 255 | 1 | 8 位无符号整型 | octet | uint8_t |
Uint8ClampedArray | 0 到 255 | 1 | 8 位无符号整型(一定在 0 到 255 之间) | octet | uint8_t |
Int16Array | -32768 到 32767 | 2 | 16 位有符号整型(补码) | short | int16_t |
Uint16Array | 0 到 65535 | 2 | 16 位无符号整型 | unsigned short | uint16_t |
Int32Array | -2147483648 到 2147483647 | 4 | 32 位有符号整型(补码) | long | int32_t |
Uint32Array | 0 到 4294967295 | 4 | 32 位无符号整型 | unsigned long | uint32_t |
Float32Array | -3.4E38 到 3.4E38 并且 1.2E-38 是最小的正数 |
4 | 32 位 IEEE 浮点数(7 位有效数字,例如 1.234567 ) |
unrestricted float | float |
Float64Array | -1.8E308 到 1.8E308 并且 5E-324 是最小的正数 |
8 | 64 位 IEEE 浮点数(16 位有效数字,例如 1.23456789012345 ) |
unrestricted double | double |
BigInt64Array | -263 到 263 - 1 | 8 | 64 位有符号整数(补码) | bigint | int64_t (signed long long) |
BigUint64Array | 0 到 264 - 1 | 8 | 64 位无符号整型 | bigint | uint64_t (unsigned long long) |
Unit8Array
指的是,把ArrayBuffer
的每个 byte(8-bit) 当作一个单独的无符号整型数字 (0 - 255)Unit16Array
表示为使用 16 bits (2 bytes) 表示一个无符号整型 (0 ~ 2^16-1) 的数的数组Int8Array
表示使用 8 bits 表示一个有符号整型 (-128 ~ 127)Float32Array
表示使用 32 bits 表示一个浮点数Unit7ClampedArray
在 0 ~ 255 范围内和Unit8Array
是一样的,对超出范围的处理有所不同,和图像处理相关(一般像素范围也是 0 ~ 255)
示例:
// 创建8个字节长度的缓存冲
const buffer = new ArrayBuffer(8);
// 将buffer转化为Uint8Array
// Uint8Array中每一个元素表示一个字节(8位)
const uint8Array = new Uint8Array(buffer);
1.2.DataView
DataView
视图是一个可以从二进制 ArrayBuffer
对象中读写多种数值类型的底层接口,使用它时,不用考虑不同平台的字节序(endianness)问题。
相较于 TypedArray
,DataView
对于 ArrayBuffer
的操作更加灵活。
在 TypedArray
中操作二进制 ArrayBuffer
时每个元素占用的字节大小是固定的,要么每个元素占用 8 位、16 位或者 32 位。
而 DataView
对于 ArrayBuffer
的操作就显得更加灵活了,我们可以通过 DataView
从 ArrayBuffer
中自由的读写多种数据类型,从而控制字节顺序。
简单来讲,想较与 TypedArray
每个元素中固定的字节大小,我们可以通过 DataView
来自由的操作 ArrayBuffer
。
创建 DataView:
new DataView(buffer [, byteOffset [, byteLength]])
// 创建8个字节长度的缓存冲
const buffer = new ArrayBuffer(8);
// 根据传入的buffer 从第一个字节开始,并且字节长度为匹配buffer的长度
const dataView = new DataView(buffer);
// 将DataView中偏移量为0个字节的字节,也就是第一个字节设置为十进制的1
dataView.setUint8(0, 1);
// 将DataView中偏移量为1个字节的字节,也就是第二个字节设置为十进制的2
dataView.setUint8(1, 2);
创建 DataView
支持传入三个参数:
buffer
:为必填,它支持传入一个ArrayBuffer
表示DataView
中的源数据。byteOffset
:选填,它表示创建DataView
时开头从buffer
的哪个字节开始,可以作为启始偏移量。未指定时,默认从第一个字节开始。btyeLength
:选填,它表示创建该DataView
时的长度,当不传递默认时表示匹配buffer
的长度。
1.3.TypedArray 和 DataView 的设计目的
ArrayBuffer
对象的各种 TypedArray
视图,是用来向网卡、声卡之类的本机设备传送数据,所以使用本机的字节序就可以了;
而 DataView
视图的设计目的,是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的。
1.4.ArrayBuffer、TypedArray、DataView 关系图
2.Blob
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream
来用于数据操作。
注意
File
对象是继承与blob
的
创建如下:
const aBlob = new Blob(array, options);
Blob()
构造函数返回一个新的 Blob
对象。 blob 的内容由参数数组中给出的值的串联组成。
通过 new Blob 可以创建一个新的 blob 对象实例,构造函数支持接受两个参数:
array
: 是一个由ArrayBuffer
,ArrayBufferView
,Blob
,DOMString
等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进Blob
。DOMStrings 会被编码为 UTF-8。options
: 是一个对象,它拥有如下属性:type
:默认值为""
,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。endings
:默认值为"transparent"
,用于指定包含行结束符\n
的字符串如何被写入。 它是以下两个值中的一个:"native"
,代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者"transparent"
,代表会保持 blob 中保存的结束符不变
常见的 MIME 类型:
MIME 类型 | 说明 |
---|---|
text/plain | 纯文本文档 |
text/html | HTML 文档 |
text/javascript | JavaScript 文件 |
text/css | CSS 文件 |
application/json | JSON 文件 |
application/pdf | PDF 文件 |
application/xml | XML 文件 |
image/jpeg | JPEG 图像 |
image/png | PNG 图像 |
image/gif | GIF 图像 |
image/svg+xml | SVG 图像 |
audio | MP3 文件 |
video | MP4 文件 |
创建一个 blob 对象:
const name = JSON.stringify({ name: "tianyichuxin" });
// 传入DOMString创建blob
const blob = new Blob([name], {
type: "application/json",
});
// log: 23 utf8中一个英文代表一个字节
console.log(blob.size);
const buffer = new ArrayBuffer(8);
// 传入ArrayBuffer创建blob
const bufferToBlob = new Blob([buffer]);
// log: 8
console.log(bufferToBlob.size);
部分方法:
slice()
从 Blob 中截取一部分并返回一个新的 Blob(用法同数组的 slice)arrayBuffer()
返回一个以二进制形式展现的 promisestream()
返回一个 ReadableStream 对象text()
返回一个文本形式的 promise
2.1.读取 blob 内容
FileReader
对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File
或 Blob
对象指定要读取的文件或数据。
const name = JSON.stringify({
name: "tianyichuxin",
}); // 传入DOMString创建blob
const blob = new Blob([name], {
type: "application/json",
});
/**
*
* @param {*} blob blob 对象
* @param {*} type 输出的结果
*/
function getBlobByType(blob, type) {
const fileReader = new FileReader(blob);
switch (type) {
// 读取文件的 ArrayBuffer 数据对象.
case "arrayBuffer":
fileReader.readAsArrayBuffer(blob);
break;
// 读取文件为的字符串
case "DOMstring":
fileReader.readAsText(blob, "utf8");
break;
// 读取文件为data: URL格式的Base64字符串
case "dataUrl":
fileReader.readAsDataURL(blob);
break;
// 读取文件为文件的原始二进制数据(已废弃不推荐使用)
case "binaryString":
fileReader.readAsBinaryString(blob);
break;
default:
break;
}
return new Promise((resolve) => {
// 当文件读取完成时候触发
fileReader.onload = (e) => {
// 获取最终读取结果
const result = e.target.result;
resolve(result);
};
});
}
// ArrayBuffer 对象
getBlobByType(blob, "arrayBuffer").then((res) => console.log(res));
// {"name":"tianyichuxin"}
getBlobByType(blob, "DOMstring").then((res) => console.log(res));
// data:application/json;base64,eyJuYW1lIjoiMTlRSW5nZmVuZyJ9
getBlobByType(blob, "dataUrl").then((res) => console.log(res));
// {"name":"tianyichuxin"}
getBlobByType(blob, "binaryString").then((res) => console.log(res));
通常情况下, File
对象是来自用户在一个 input
元素上选择文件后返回的 FileList
对象,也可以是来自由拖放操作生成的 DataTransfer
对象,或者来自 HTMLCanvasElement
上的 mozGetAsFile
() API。
简单来说,File 就是基于 Blob 而来。它拥有 Blob 的所有功能的同时扩展了一系列关于文件的属性。
3.File
File
就是文件,继承自 Blob
,也是二进制对象,也有自己特有的属性和方法,通常用在<input type="file">
选择的 FileList
对象,或者是使用拖拽操作产生的的 DataTransfer
对象。
File
对象是特殊类型的 Blob
,且可以用在任意的 Blob 类型的 context 中。比如说, FileReader
, URL.createObjectURL()
, createImageBitmap()
, 及 XMLHttpRequest.send()
都能处理 Blob
和 File
。
创建如下:
const file = new File(array, name[, options])
array
:是一个由ArrayBuffer
,ArrayBufferView
,Blob
,DOMString
等对象构成,DOMStrings
会被编码为 UTF-8。name
:表示文件名称,或者文件路径。options
:是一个可选,它可能会指定如下两个属性:type
:默认值为""
,内容的 MIME 类型。lastModified
:数值,表示文件最后修改时间的 Unix 时间戳(毫秒)。默认值为Date.now()
。
3.1.ArrayBuffer 和 Blob/File 的关系
4.ObjectURL
ObjectURL
是用于引用任何数据的简单 URL 字符串,即用一个 URL 来访问某个数据资源。比如可以创建某个图片的 ObjectURL
值,然后设置该值为图片标签的 src,即可展示该图片。
ObjectURL
本质上是一个临时的,用于访问某个资源的路径。它常常用来展示文件,比如图片、Excel、PDF 文件、视频文件等。
大多数情况下,我们可以看到一些网页内部可以看到一些诸如此类的 Blob Url:
<video src="blod:https://www.baidu.com/dsadafa78HJKHGSasd2452kS98sds"
关于 Blob URL/Object URL
其实它们是一种伪协议,允许将 Blob
和 File
对象用作图像、二进制数据下载链接等的 URL
源。它的好处其实有很多。
平常我们并不可以直接处理 Image 标签之类的原始二进制数据,所以对于图片等需要 url 作为源的标签通常做法是将图片上传到服务器上得到一个 url 从而通过 URL
加载二进制数据。
与其上传二进制数据,然后通过 URL
将其返回,不如使用 Blob/Object Url
无需额外的步骤,使用浏览器本地 Api 即可直接访问数据而不需要通过服务器来上传数据。
相比Base64
字符串编码也可以解决上述问题, Blob
是纯二进制字节数组,不会像 Data-URI
那样有任何显着的开销,这使得它们处理起来更快更小。
同时这些 URL
只能在浏览器的单个实例和同一会话(即页面/文档的生命周期)中本地使用,这意味者离开当前浏览器实例这个 URL
就会失效。
我们可以通过 URL.createObjectURL(object)
来创建对应的 Object URL
,这个方法会返回一个 DOMString
字符串,其中包含一个表示参数中给出的对象的URL
。
同时,这个 URL
的生命周期和创建它的窗口中的 document
绑定。这个新的 URL
对象表示指定的 File
对象或 Blob
对象。
在创建时候它会接受一个参数:
- object 表示用于创建 URL 的
File
对象、Blob
对象或者MediaSource
对象。
同样它会返回一个DOMString
包含了一个对象URL
,该 URL 可用于指定源 object 的内容。
返回的 DOMString
格式为 blob:<origin>/<uuid>
。
当在你的网页上不再使用通过
URL.createObjectURL(object)
创建的 URL 时,记得使用URL.revokeObjectURL(url)
来主动释放它们。
通过 Object URL 下载图片:
HTML 部分代码
<input type="file" id="input" /> <img id="img" />
js 部分代码
const input = document.getElementById("input");
input.onchange = (e) => {
const url = URL.createObjectURL(e.target.files[0]);
// 实现下载
const a = document.createElement("a");
a.href = url;
a.download = "img";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
const name = JSON.stringify({
name: "tianyichuxin",
});
通过 Blob
下载:
// 传入DOMString创建blob
const blob = new Blob([name], {
type: "application/json",
});
// 创建 Object Url
const url = URL.createObjectURL(blob);
const aLink = document.createElement("a");
// href属性
aLink.href = url;
// 定义下载的文件名
aLink.download = "name.json";
// 派发a链接的点击事件
aLink.dispatchEvent(new MouseEvent("click"));
// 下载完成后,释放 URL.createObjectURL() 创建的 URL 对象。
URL.revokeObjectURL(url);
4.1.ArrayBuffer 和 Blob 、Object URL 的关系
5.Buffer
在 Node.JS 中,Buffer
是一种全局对象,用于表示二进制数据。Buffer
对象可以直接访问和修改其内容,因此它比Blob
和ArrayBuffer
更适合网络编程或文件系统操作。
在Node.js v6.0之前创建
Buffer
对象直接使用new Buffer()
构造函数来创建对象实例,但是Buffer
对内存的权限操作相比很大,可以直接捕获一些敏感信息。例如,new Buffer(arg)
的构造函数可以接受多种类型的参数,这可能导致意外的行为,尤其是在处理用户提供的输入时。所以在v6.0以后,官方文档里面
new Buffer()
构造函数被弃用,并在后续版本中被完全移除。建议使用Buffer.from()
接口去创建Buffer
对象。
const data = "Hello, world!";
const buffer = Buffer.from(data, "utf8");
ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区,是一个字节数组,可读但不可直接写。
Buffer
是 Node.JS 中用于操作 ArrayBuffer
的视图,是 TypedArray
的一种。是 Uint8Array
实例,但是与 TypedArray
有微小的不同。(buffer 可以类比为视图)Buffer.from()
与 TypedArray.from()
有着不同的实现。
具体来说,typedArray.form()
可接受第二个参数为映射函数,Buffer.form()
不行。
创建的常用方法:
Buffer.from(array)
:返回包含给定八位字节数组的副本的新 bufferBuffer.from(buffer)
:返回包含给定 buffer 副本的新 bufferBuffer.from(arrayBuffer[, betyOffset[, len]])
:返回与给定 arrayBuffer 共享同一段内存的新 BufferBuffer.from(string)
:返回包含给定字符串副本的新 BufferBuffer.alloc(size)
:返回一个指定大小的新建的已经初始化的 Buffer
5.1.Buffer 转 ArrayBuffer
使用Buffer
的.buffer
属性即可
const buffer = Buffer.from("Hello, world!", "utf8");
const arrayBuffer = buffer.buffer;
5.2.ArrayBuffer 转 Buffer
使用Buffer.from()
静态方法
const arrayBuffer = new ArrayBuffer(8);
const buffer = Buffer.from(arrayBuffer);
6.AudioBuffer
ArrayBuffer
里面的数据并没有分类,AudioBuffer
接口表示存在内存里的一段短小的音频资源,利用 AudioContext
。decodeAudioData()
方法从一个音频文件构建,或者利用 AudioContext.createBuffer()
从原始数据构建。把音频放入 AudioBuffer
后,可以传入到一个 AudioBufferSourceNode
进行播放。
从一个音频文件构建AudioCOntext.decodeAudioData()
-放入->AudioBuffer
-播放->AudioBufferSourceNode
从原始数据构建AudioCOntext.createBuffer()
-放入->AudioBuffer
-播放->AudioBufferSourceNode
创建如下:
var audioCtx = new AudioContext();
audioCtx.decodeAudioData(arrBuffer, function (audioBuffer) {
// audioBuffer就是AudioBuffer
});
属性:
AudioBuffer.sampleRate
:存储在缓存区的 PCM 数据的采样率:浮点数,单位为 sample/s。AudioBuffer.length
:返回存储在缓存区的 PCM 数据的采样帧率:整形。AudioBuffer.duration
:返回存储在缓存区的 PCM 数据的时长:双精度型(单位为秒)。AudioBuffer.numberOfChannels
:返回存储在缓存区的 PCM 数据的通道数:整形。
方法:
AudioBuffer.getChannelData()
:返回一个 Float32Array,包含了带有频道的 PCM 数据,由频道参数定义(有 0 代表第一个频道)AudioBuffer.copyFromChannel()
:从 AudioBuffer 的指定频道复制到数组终端。AudioBuffer.copyToChannel()
:复制样品到原数组的 AudioBuffer 的指定频道
7.Base64
Base64
是一组相似的二进制到文本(binary-to-text)的编码规则。一个常见应用是对二进制数据进行编码,以便将其纳入 dataURL
中
在前端经常会碰到,格式是 data:[<mediatype>][;base64],<data>
在 JavaScript 中,有两个函数被分别用来处理解码和编码 Base64
字符串:
atob()
:解码通过Base-64
编码的字符串数据(“atob”应读作“ASCII to binary”)btoa()
:从二进制数据“字符串”创建一个Base-64
编码的 ASCII 字符串(“btoa”应读作“binary to ASCII”)
优点:
- 可以将二进制数据(比如图片)转化为可打印字符,方便传输数据。
- 对数据进行简单的加密,肉眼是安全的。
- 如果是在 html 或者 css 处理图片,可以减少 http 请求。
缺点:
- 内容编码后体积变大, 至少大 1/3。因为是三字节变成四个字节,当只有一个字节的时候,也至少会变成三个字节。
- 编码和解码需要额外工作量。
用 Bse64 展示图片:
HTML 部分代码
<!-- 读取文件,用 Bse64 展示图片 -->
<input type="file" id="input" />
<img id="img" />
js 部分代码
const input = document.getElementById("input");
const img = document.getElementById("img");
input.onchange = (e) => {
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = (e) => {
img.src = e.target.result;
console.log(e.target.result);
};
};
8.FileReader
FileReader
对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容
以下为 FileReader
的对象方法:
方法 | 说明 |
---|---|
FileReader.abort() | 中止读取操作。在返回时,readyState 属性为 DONE |
FileReader.readAsArrayBuffer() | 开始读取指定的 Blob 中的内容。完成后,result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象。 |
FileReader.readAsBinaryString() | 开始读取指定的 Blob 中的内容。完成后,result 属性中将包含所读取文件的原始二进制 数据 |
FileReader.readAsDataURL() | 开始读取指定的 Blob 中的内容。完成后,result 属性中将包含一个 data: URL 格式的 Base64 字符串以表示所读取文件的内容 |
FileReader.readAsText() | 开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含一个字符串 以表示所读取的文件内容 |
二、相关转换
1.Blob 与 File 互转
1.1.Blob 转 File
File
对象其实是特殊类型的 Blob
,且可以用在任意的 Blob 类型的上下文中。比如说,FileReader
, URL.createObjectURL()
, createImageBitmap()
, 及 XMLHttpRequest.send()
都能处理 Blob
和 File
。
File
接口也继承了 Blob
接口的属性。这两个东西互转感觉没必要,如果要转的话,可以利用 FileReader
作为桥梁,先转成 ArrayBuffer
,然后在转成相应的 Blob
或者 File
。
const blob = new Blob(["blob文件"], { type: "text/plain" });
// blob 转 file
const file = new File([blob], "test", { type: blob.type });
console.log("file:", file);
1.2.File 转 Blob
const file = new File(["文件对象"], "test", { type: "text/plain" });
// file 转 blob
const blob = new Blob([file], { type: file.type });
console.log("blob:", blob);
2.File、Blob、img 与 Base64 互转
2.1.File、Blob 转 Base64
//file为 File对象 或 Blob对象
function fileToDataURL(file) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
return reader.result;
};
}
2.2.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);
};
});
};
// 获取base64
img2base64("./logo.png").then((res) => {
console.log(res);
});
2.3.Base64 转 Blob、File
// Base64 转为 Blob
function dataURLToBlob(fileDataUrl) {
let arr = fileDataUrl.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(fileDataUrl, filename) {
let arr = fileDataUrl.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 });
}
3.File、Blob 转 Object URL
// object:用于创建 URL 的 File 对象、Blob 对象
const objectUrl = URL.createObjectURL(object);
4.File、Blob 与 ArrayBuffer 互转
File/Blob
转成 ArrayBuffer
需要借助 FileReader
类。
4.1.File/Blob 转 ArrayBuffer
// file为 File对象 或 Blob对象
function fileToArrayBuffer(file) {
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = () => {
return reader.result;
};
}
ArrayBuffer
转成 File/Blob
直接调用 new File/Blob
构造函数
4.2.ArrayBuffer 转 File
function bufToFile(buf, filename) {
return new File([buf], filename);
}
4.3.ArrayBuffer 转 blob
function bufToBlob(buf, mimeType = "") {
return new Blob([buf], { type: mimeType });
}
Blob
函数的第二个参数与 File
函数的第二个参数略有不同,Blob
是一个对象,对象中有一个 type 属性,默认值为 ""
,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。Blob
的第一个参数也是一个由 ArrayBuffer
, ArrayBufferView
, Blob
, DOMString
等对象构成的数组。
DOMString
是 DOM 字符串,比如:hey!
。它的 type 则是:text/html
。
5.Blob 转 audio
使用URL.createObjectURL
和 new Audio
进行转换
const url = URL.createObjectURL(blob);
const audioUrl = new Audio(url);
6.File 转 audioBuffer
在 Web 网页中,用户选择的文件是个 file 对象,我们可以将这个文件对象转换成 Blob、ArrayBuffer 或者 Base64。
在音频处理这里,都是使用ArrayBuffer
这个数据类型。
file.onchange = function (event) {
var file = event.target.files[0];
// 开始识别
var reader = new FileReader();
reader.onload = function (event) {
var arrBuffer = event.target.result;
// arrBuffer就是包含音频数据的ArrayBuffer对象
};
reader.readAsArrayBuffer(file);
};
使用的是readAsArrayBuffer()
方法,无论是 MP3 格式、OGG 格式还是 WAV 格式,都可以转换成 ArrayBuffer 类型。
7.audioBuffer 转 Blob
export function audioBufferToWav(buffer: AudioBuffer, opt?: any) {
opt = opt || {};
const numChannels = buffer.numberOfChannels;
const sampleRate = opt.sampleRate || buffer.sampleRate;
const format = opt.float32 ? 3 : 1;
const bitDepth = format === 3 ? 32 : 16;
let result;
if (numChannels === 2) {
result = interleave(buffer.getChannelData(0), buffer.getChannelData(1));
} else {
result = buffer.getChannelData(0);
}
return encodeWAV(result, format, sampleRate, numChannels, bitDepth);
}
function encodeWAV(samples: Float32Array, format: number, sampleRate: number, numChannels: number, bitDepth: number) {
const bytesPerSample = bitDepth / 8;
const blockAlign = numChannels * bytesPerSample;
let buffer = new ArrayBuffer(44 + samples.length * bytesPerSample);
let view = new DataView(buffer);
writeString(view, 0, "RIFF");
view.setUint32(4, 36 + samples.length * bytesPerSample, true);
writeString(view, 8, "WAVE");
writeString(view, 12, "fmt ");
view.setUint32(16, 16, true);
view.setUint16(20, format, true);
view.setUint16(22, numChannels, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * blockAlign, true);
view.setUint16(32, blockAlign, true);
view.setUint16(34, bitDepth, true);
writeString(view, 36, "data");
view.setUint32(40, samples.length * bytesPerSample, true);
if (format === 1) {
floatTo16BitPCM(view, 44, samples);
} else {
writeFloat32(view, 44, samples);
}
return buffer;
}
function interleave(inputL: Float32Array, inputR: Float32Array) {
let length = inputL.length + inputR.length;
let result = new Float32Array(length);
let index = 0;
let inputIndex = 0;
while (index < length) {
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
function writeFloat32(output: DataView, offset: number, input: Float32Array) {
for (let i = 0; i < input.length; i++, offset += 4) {
output.setFloat32(offset, input[i], true);
}
}
function floatTo16BitPCM(output: DataView, offset: number, input: Float32Array) {
for (let i = 0; i < input.length; i++, offset += 2) {
let s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
}
}
function writeString(view: DataView, offset: number, string: string) {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
Arraybuffer 加头
const _writeString = (view: DataView, offset: number = 0, value: string = "") => {
_.forEach(value.split(""), (char) => view.setUint8(offset++, char.charCodeAt(0)));
};
export const AddRiffHeader = (data: ArrayBuffer, sampleRate: number = 16000, numberOfChannels: number = 1, bitsPerSample = 16) => {
const headerSize = 44;
const dataID = "data";
const dataSize = data.byteLength;
const fmtChunkID = "fmt ";
const fmtPcmChunkSize = 16;
const audioPcmFormat = 1;
const blockAlign = bitsPerSample / 8;
const riffChunkID = "RIFF";
const riffChunkSize = 4 + (8 + fmtPcmChunkSize) + (8 + dataSize);
const riffChunkFormat = "WAVE";
const header = new ArrayBuffer(headerSize);
const headerView = new DataView(header);
_writeString(headerView, 0, riffChunkID);
headerView.setUint32(4, riffChunkSize, true);
_writeString(headerView, 8, riffChunkFormat);
_writeString(headerView, 12, fmtChunkID);
headerView.setUint32(16, fmtPcmChunkSize, true);
headerView.setUint16(20, audioPcmFormat, true);
headerView.setUint16(22, numberOfChannels, true);
headerView.setUint32(24, sampleRate, true);
headerView.setUint32(28, sampleRate * blockAlign, true);
headerView.setUint16(32, blockAlign, true);
headerView.setUint16(34, bitsPerSample, true);
_writeString(headerView, 36, dataID);
headerView.setUint32(40, dataSize, true);
const result = new Uint8Array(header.byteLength + data.byteLength);
result.set(new Uint8Array(header), 0);
result.set(new Uint8Array(data), header.byteLength);
return result.buffer;
};
8.AudioBuffer 转换成 WAV 格式
使用 npm 已有包
安装包:npm install audiobuffer-to-wav --save
用例:
var toWav = require("audiobuffer-to-wav");
var xhr = require("xhr");
var context = new AudioContext();
// request the MP3 as binary arraybuffer
xhr(
{
uri: "audio/track.mp3",
responseType: "arraybuffer",
},
function (err, body, resp) {
if (err) throw err;
// decode the MP3 arraybuffer into an AudioBuffer
audioContext.decodeAudioData(resp, function (buffer) {
// encode AudioBuffer to WAV ArrayBuffer
var wav = toWav(buffer); //实际上是在audio前加wav的头
// do something with the WAV ArrayBuffer ...
});
}
);
9.ArrayBuffer 转 AudioBuffer
这里的ArrayBuffer
相对于把音频文件数组化了,ArrayBuffer 里面的数据并没有分类,统一分解了,想要准确提取某一截音频数据,提取不出来,才需要转换成 AudioBuffer,AudioBuffer
是一个仅仅包含音频数据的数据对象,是 Web Audio API 中的一个概念。
使用 AudioContext 对象的decodeAudioData()
方法,代码如下:
var audioCtx = new AudioContext();
audioCtx.decodeAudioData(arrBuffer, function (audioBuffer) {
// audioBuffer就是AudioBuffer
});
创建一个空的 AudioBuffer,复制现有的通道数据前 3 秒的数据,然后复制的内容写入到这个空的 AudioBuffer
// 声道数量和采样率
var channels = audioBuffer.numberOfChannels;
var rate = audioBuffer.sampleRate;
// 截取前3秒
var startOffset = 0;
var endOffset = rate * 3;
// 3秒对应的帧数
var frameCount = endOffset - startOffset;
// 创建同样采用率、同样声道数量,长度是前3秒的空的AudioBuffer
var newAudioBuffer = new AudioContext().createBuffer(channels, endOffset - startOffset, rate);
// 创建临时的Array存放复制的buffer数据
var anotherArray = new Float32Array(frameCount);
// 声道的数据的复制和写入
var offset = 0;
for (var channel = 0; channel < channels; channel++) {
audioBuffer.copyFromChannel(anotherArray, channel, startOffset);
newAudioBuffer.copyToChannel(anotherArray, channel, offset);
}
// newAudioBuffer就是全新的复制的3秒长度的AudioBuffer对象
总结:
以上相关流之间的各种转化,可根据需要在下面图片中找对应的例子