找回密码
 立即注册

QQ登录

只需一步,快速开始

Winny

超级版主

141

主题

261

帖子

1682

积分

超级版主

Rank: 8Rank: 8

积分
1682
Winny
超级版主   /  发表于:2022-7-28 16:22  /   查看:1905  /  回复:0
本帖最后由 Winny 于 2022-7-28 16:22 编辑

在Web开发中,当我们处理文件时,经常会遇到二进制数据,这是因为JavaScript对于二进制操作性能更高一些。不过,在JavaScript中会有多种二进制数据格式,例如ArrayBuffer,Uint8Array,DataView,Blob,File等等。基本的二进制对象是ArrayBuffer,是所有其它类型的基础。可以使用如下代码来创建它:
  1. // 创建一个长度为16的buffer
  2. let buffer = new ArrayBuffer(16)
  3. buffer.byteLength
复制代码
它会分配一个16字节的连续内存控件,并用0进行预填充。ArrayBuffer与Array没有任何共同之处,ArrayBuffer长度是固定的,创建之后无法增加或减少它的长度,是一个原始的字节序列,里边存储的具体是什么无从知晓,需要使用其它工具来解析,这个解析工具就是衍生出的其它二进制数据类型。

1.  TypedArray系列

    Uint8Array —— 将 ArrayBuffer 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。
    Uint16Array —— 将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。
    Uint32Array —— 将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。
    Float64Array —— 将每 8 个字节视为一个 5.0x10-324 到 1.8x10308 之间的浮点数。

因此,一个 16 字节 ArrayBuffer 中的二进制数据可以解释为 16 个“小数字”,或 8 个更大的数字(每个数字 2 个字节),或 4 个更大的数字(每个数字 4 个字节),或 2 个高精度的浮点数(每个数字 8 个字节)
1658915271172.png683132323.png
我们已Uint32Array为例,基于长度为16的ArrayBuffer创建的Uint32Array长度会为4:
image.png328348082.png

2. DataView

DataView语法如下:
  1. new DataView(buffer, [byteOffset], [byteLength])
复制代码
各参数含义如下:
buffer —— 底层的 ArrayBuffer。与类型化数组不同,DataView 不会自行创建缓冲区(buffer)。我们需要事先准备好。
byteOffset —— 视图的起始字节位置(默认为 0)。
byteLength —— 视图的字节长度(默认至 buffer 的末尾)。

通过DataView,可以使用.getUint8(i)或.getUint16(i)之类的方法访问数据,在实际调用时选择格式,而不是在构造时。
image.png440859069.png

3. TextDecoder&TextEmcoder

之前两种形式都属于数字,如果二进制数据实际上是一个字符串,我们就需要使用TextDecoder对象在给定缓冲区和编码格式,读取实际的字符串,核心API如下:
  1. let decoder = new TextDecoder([label], [options]);
复制代码
label —— 编码格式,默认为 utf-8,但同时也支持 big5,windows-1251 等许多其他编码格式。
options —— 可选对象:
    fatal —— 布尔值,如果为 true 则为无效(不可解码)字符抛出异常,否则(默认)用字符 \uFFFD 替换无效字符。
    ignoreBOM —— 布尔值,如果为 true 则 BOM(可选的字节顺序 Unicode 标记),很少需要使用。

  1. let str = decoder.decode([input], [options]);
复制代码
input —— 要被解码的 BufferSource。
options —— 可选对象:
    stream —— 对于解码流,为 true,则将传入的数据块(chunk)作为参数重复调用 decoder。在这种情况下,多字节的字符可能偶尔会在块与块之间被分割。这个选项告诉 TextDecoder 记住“未完成”的字符,并在下一个数据块来的时候进行解码。

image.png634520568.png
TextEncoder做的是一件相反的事情,用来将字符串转为字节。核心语法为:
  1. let encoder = new TextEncoder();
复制代码
TextEncoder只支持 utf-8 编码,它有两个方法:
encode(str) —— 从字符串返回 Uint8Array。
encodeInto(str, destination) —— 将 str 编码到 destination 中,该目标必须为 Uint8Array。

image.png64382323.png

4. Blob
ArrayBuffer属于ECMA标准的一部分,是JavaScript的一部分。在浏览器中,还有其它更高级的对象,特别是Blob。Blob是由一个可选的字符串type(通常是MIME类型)和blobParts组成。
image.png534336242.png
构造函数的语法为:
  1. new Blob(blobParts, options);
复制代码
blobParts 是 Blob/BufferSource/String 类型的值的数组。
options 可选对象:
    type —— Blob 类型,通常是 MIME 类型,例如 image/png,
    endings —— 是否转换换行符,使 Blob 对应于当前操作系统的换行符(\r\n 或 \n)。默认为 "transparent"(啥也不做),不过也可以是 "native"(转换)。

image.png341218566.png

Blob转URL

Blob也可以转化成<a>,<img>或其它标签的URL,来显示Blob中的内容。而Blob中的type,可以让我们下载上传Blob对象。在网络请求中,type自然的变成了Content-Type。
image.png993383483.png
浏览器内部为每个通过URL.createObjectURL生成的URL存储了一个URL到Blob的映射,因此,此类URL很短,但是可以访问Blob。生成的URL仅在当前文档打开的状态下才有效。但是这种也是有一定的副作用的,虽然这里有Blob的映射,但Blob本身只保存在内存中,浏览器无法释放它。只有在文档unload时,该映射才会被自动清除,Blob随之会被释放。但是如果程序声明较长,那么这个释放就不会很快发生。也就是说,如果我们创建了一个URL,即使我们不再需要Blob了,它也会挂载内存中。调用delete(blob)也会返回false。而URL.revokeObjectURL(url)就可以删除对应的url与blob的映射,从而尽早的进行资源回收。

Blob转base64

base64是一种常见的编码形式,它将二进制数据表示为一个由0到64的ASCII码组成的字符串,比较安全。更重要的是它可以直接在“data-url”中使用此编码。“data-url” 的形式为 data:[<mediatype>][;base64],<data>。我们可以在任何地方使用这种 url,和使用“常规” url 一样。 image.png359194945.png
这两种从Blob创建URL的方法都可以用,但通常URL.createObjectURL(blob)更加快捷简单。使用URL.createObjectURL(blob)时,如果介意内存,在URL使用完成之后需要移除URL,对应的Blob可以直接访问。而Blob转为DataUrl时,不需要任何撤销行为,但对大的Blob进行编码时,性能与内存会有损耗。

Blob转ArrayBuffer

Blob构造器允许从几乎任何东西创建blob,包括BufferSource。在需要窒息感低级别处理时,可以使用blob.arrayBuffer()获取低级别的ArrayBuffer。
  1. // 从 bolb 获取 arrayBuffer
  2. const bufferPromise = await blob.arrayBuffer();
复制代码


Blob转Stream

当读取和写入超过2G的blob时,将其转换为ArrayBuffer会更占用内存,这是可以将blob转为stream进行处理。Blob接口的stream方法返回一个ReadableStream,在被读取时可以返回Blob中包含的数据。
  1. // 从 blob 获取可读流(readableStream)
  2. const readableStream = blob.stream();
  3. const stream = readableStream.getReader();

  4. while (true) {
  5.   // 对于每次迭代:value 是下一个 blob 数据片段
  6.   let { done, value } = await stream.read();
  7.   if (done) {
  8.     // 读取完毕,stream 里已经没有数据了
  9.     console.log('all blob processed.');
  10.     break;
  11.   }

  12.   // 对刚从 blob 中读取的数据片段做一些处理
  13.   console.log(value);
  14. }
复制代码

File

File对象继承Blob,并扩展了与文件系统相关的功能。有两种获取File对象的方法,第一种是使用构造器:
  1. new File(fileParts, fileName, [options])
复制代码

fileParts —— Blob/BufferSource/String 类型值的数组。
fileName —— 文件名字符串。
options —— 可选对象:
    lastModified —— 最后一次修改的时间戳(整数日期)
第二种是常见的从<input type="file"> 或拖放或其他浏览器接口来获取文件,在这种情况下,file将从操作系统OS来获得this信息。由于File继承Blob,所以File对象具备与Blob相同的属性,附加了name、lastModified额外属性。
  1. <input type="file" onchange="showFile(this)">

  2. <script>
  3. function showFile(input) {
  4.   let file = input.files[0];

  5.   alert(`File name: ${file.name}`); // 例如 my.png
  6.   alert(`Last modified: ${file.lastModified}`); // 例如 1552830408824
  7. }
  8. </script>
复制代码
FileReader 是一个对象,其唯一目的是从 Blob(因此也从 File)对象中读取数据。它使用事件来传递数据,因为从磁盘读取数据可能比较费时间。
构造函数:

  1. let reader = new FileReader()
复制代码
主要方法:
readAsArrayBuffer(blob) —— 将数据读取为二进制格式的 ArrayBuffer。
readAsText(blob, [encoding]) —— 将数据读取为给定编码(默认为 utf-8 编码)的文本字符串。
readAsDataURL(blob) —— 读取二进制数据,并将其编码为 base64 的 data url。
abort() —— 取消操作。
readAsArrayBuffer —— 用于二进制文件,执行低级别的二进制操作。对于诸如切片(slicing)之类的高级别的操作,File 是继承自 Blob 的,所以我们可以直接调用它们,而无需读取。
readAsText —— 用于文本文件,当我们想要获取字符串时。
readAsDataURL —— 当我们想在 src 中使用此数据,并将其用于 img 或其他标签时。正如我们在 Blob 一章中所讲的,还有一种用于此的读取文件的替代方案:URL.createObjectURL(file)。
读取过程中,有以下事件:
loadstart —— 开始加载。
progress —— 在读取过程中出现。
load —— 读取完成,没有 error。
abort —— 调用了 abort()。
error —— 出现 error。
loadend —— 读取完成,无论成功还是失败。
读取完成后,我们可以通过以下方式访问读取结果:
reader.result 是结果(如果成功)
reader.error 是 error(如果失败)。
使用最广泛的事件无疑是 load 和 error。
这是一个读取文件的示例:

  1. <input type="file" onchange="readFile(this)">

  2. <script>
  3. function readFile(input) {
  4.   let file = input.files[0];

  5.   let reader = new FileReader();

  6.   reader.readAsText(file);

  7.   reader.onload = function() {
  8.     console.log(reader.result);
  9.   };

  10.   reader.onerror = function() {
  11.     console.log(reader.error);
  12.   };

  13. }
  14. </script>
复制代码








0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部