💴现代 JavaScript —— 杂项(二进制 数据 & 文件 )
00 分钟
2022-9-9
2022-9-15
type
status
date
slug
summary
tags
category
icon
password
Edited
Sep 15, 2022 01:17 PM
Created
Sep 13, 2022 07:45 AM

二进制 数据 & 文件

基本的二进制对象是 ArrayBuffer —— 对固定长度的连续内存空间的引用。
ArrayBuffer 是一个内存区域。它里面存储了什么?无从判断。只是一个原始的字节序列。
如要操作 ArrayBuffer,我们需要使用“视图”对象。
视图对象本身并不存储任何东西。它是一副“眼镜”,透过它来解释存储在 ArrayBuffer 中的字节。
例如:
  • Uint8Array —— 将 ArrayBuffer 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位,因此只能容纳那么多)。这称为 “8 位无符号整数”。
  • Uint16Array —— 将每 2 个字节视为一个 0 到 65535 之间的整数。这称为 “16 位无符号整数”。
  • Uint32Array —— 将每 4 个字节视为一个 0 到 4294967295 之间的整数。这称为 “32 位无符号整数”。
  • Float64Array —— 将每 8 个字节视为一个 5.0x10324 到 1.8x10308 之间的浮点数。
ArrayBuffer 是核心对象,是所有的基础,是原始的二进制数据。
但是,如果我们要写入值或遍历它,基本上几乎所有操作 —— 我们必须使用视图(view)

TypedArray

所有这些视图(Uint8ArrayUint32Array 等)的通用术语是 TypedArray。它们共享同一方法和属性集。
请注意,没有名为 TypedArray 的构造器,它只是表示 ArrayBuffer 上的视图之一的通用总称术语
参数有 5 种变体:
  • Uint8ArrayUint16ArrayUint32Array —— 用于 8、16 和 32 位的整数。
    • Uint8ClampedArray —— 用于 8 位整数,在赋值时便“固定“其值(见下文)。
  • Int8ArrayInt16ArrayInt32Array —— 用于有符号整数(可以为负数)。
  • Float32ArrayFloat64Array —— 用于 32 位和 64 位的有符号浮点数。

TypedArray 方法

TypedArray 具有常规的 Array 方法,但有个明显的例外。
我们可以遍历(iterate),mapslicefind 和 reduce 等。
但有几件事我们做不了:
  • 没有 splice —— 我们无法“删除”一个值,因为类型化数组是缓冲区(buffer)上的视图,并且缓冲区(buffer)是固定的、连续的内存区域。我们所能做的就是分配一个零值。
  • 无 concat 方法。
还有两种其他方法:
  • arr.set(fromArr, [offset]) 从 offset(默认为 0)开始,将 fromArr 中的所有元素复制到 arr
  • arr.subarray([begin, end]) 创建一个从 begin 到 end(不包括)相同类型的新视图。这类似于 slice 方法(同样也支持),但不复制任何内容 —— 只是创建一个新视图,以对给定片段的数据进行操作。

DataView

DataView 是在 ArrayBuffer 上的一种特殊的超灵活“未类型化”视图。它允许以任何格式访问任何偏移量(offset)的数据。
  • 对于类型化的数组,构造器决定了其格式。整个数组应该是统一的。第 i 个数字是 arr[i]
  • 通过 DataView,我们可以使用 .getUint8(i) 或 .getUint16(i) 之类的方法访问数据。我们在调用方法时选择格式,而不是在构造的时候。
  • buffer —— 底层的 ArrayBuffer。与类型化数组不同,DataView 不会自行创建缓冲区(buffer)。我们需要事先准备好。
  • byteOffset —— 视图的起始字节位置(默认为 0)。
  • byteLength —— 视图的字节长度(默认至 buffer 的末尾)。

两个术语,用于对二进制数据进行操作的方法的描述:
  • ArrayBufferView 是所有这些视图的总称。
  • BufferSource 是 ArrayBuffer 或 ArrayBufferView 的总称。
notion image

TextDecoder & TextEncoder

TextDecoder

TextDecoder 对象在给定缓冲区(buffer)和编码格式(encoding)的情况下,允许将值读取为实际的 JavaScript 字符串。
  • label —— 编码格式,默认为 utf-8,但同时也支持 big5windows-1251 等许多其他编码格式。
  • options —— 可选对象:
    • fatal —— 布尔值,如果为 true 则为无效(不可解码)字符抛出异常,否则(默认)用字符 \uFFFD 替换无效字符。
    • ignoreBOM —— 布尔值,如果为 true 则 BOM(可选的字节顺序 Unicode 标记),很少需要使用。
  • input —— 要被解码的 BufferSource
  • options —— 可选对象:
    • stream —— 对于解码流,为 true,则将传入的数据块(chunk)作为参数重复调用 decoder。在这种情况下,多字节的字符可能偶尔会在块与块之间被分割。这个选项告诉 TextDecoder 记住“未完成”的字符,并在下一个数据块来的时候进行解码。
    •  
示例:

TextEncoder

TextEncoder 做相反的事情 —— 将字符串转换为字节。
只支持 utf-8 编码。
它有两种方法:
  • encode(str) —— 从字符串返回 Uint8Array
  • encodeInto(str, destination) —— 将 str 编码到 destination 中,该目标必须为 Uint8Array

Blob

Blob 由一个可选的字符串 type(通常是 MIME 类型)和 blobParts 组成 —— 一系列其他 Blob 对象,字符串和 BufferSource
notion image
  • blobParts 是 Blob/BufferSource/String 类型的值的数组。
  • options 可选对象:
    • type —— Blob 类型,通常是 MIME 类型,例如 image/png
    • endings —— 是否转换换行符,使 Blob 对应于当前操作系统的换行符(\r\n 或 \n)。默认为 "transparent"(啥也不做),不过也可以是 "native"(转换)。
用 slice 方法来提取 Blob 片段:
  • byteStart —— 起始字节,默认为 0。
  • byteEnd —— 最后一个字节(专有,默认为最后)。
  • contentType —— 新 blob 的 type,默认与源 blob 相同。
ℹ️
Blob 对象是不可改变的
我们无法直接在 Blob 中更改数据,但我们可以通过 slice 获得 Blob 的多个部分,从这些部分创建新的 Blob 对象,将它们组成新的 Blob,等。

Blob 用作 URL

多亏了 type,让我们也可以下载/上传 Blob 对象,而在网络请求中,type 自然地变成了 Content-Type
Blob 可以很容易用作 <a><img> 或其他标签的 URL,来显示它们的内容。
URL.createObjectURL 取一个 Blob,并为其创建一个唯一的 URL,形式为 blob:<origin>/<uuid>
不过它有个副作用。虽然这里有 Blob 的映射,但 Blob 本身只保存在内存中的。浏览器无法释放它。
因此,如果我们创建一个 URL,那么即使我们不再需要该 Blob 了,它也会被挂在内存中。
URL.revokeObjectURL(url) 从内部映射中移除引用,因此允许 Blob 被删除(如果没有其他引用的话),并释放内存。

Blob 转换为 base64

URL.createObjectURL 的一个替代方法是,将 Blob 转换为 base64-编码的字符串。
这种编码将二进制数据表示为一个由 0 到 64 的 ASCII 码组成的字符串,非常安全且“可读“。更重要的是 —— 我们可以在 “data-url” 中使用此编码。
“data-url” 的形式为 data:[<mediatype>][;base64],<data>。我们可以在任何地方使用这种 url,和使用“常规” url 一样。
使用内建的 FileReader 对象来将 Blob 转换为 base64。它可以将 Blob 中的数据读取为多种格式。
URL.createObjectURL(blob)
  • 如果介意内存,我们需要撤销(revoke)它们
  • 直接访问 Blob,无需“编码/解码”
Blob 转换为 data url
  • 无需撤销(revoke)任何操作。
  • 对大的 Blob 进行编码时,性能和内存会有损耗。

Image 转换为 blob

图像操作是通过 <canvas> 元素来实现的:
  1. 使用 canvas.drawImage 在 canvas 上绘制图像(或图像的一部分)。
  1. 调用 canvas 方法 .toBlob(callback, format, quality) 创建一个 Blob,并在创建完成后使用其运行 callback
可以将callback换成async/await

Blob 转换为 ArrayBuffer

Blob 转换为 Stream

当我们读取和写入超过 2 GB 的 blob 时,将其转换为 arrayBuffer 的使用对我们来说会更加占用内存。这种情况下,我们可以直接将 blob 转换为 stream 进行处理。
stream 是一种特殊的对象,我们可以从它那里逐部分地读取(或写入)。
Blob 接口里的 stream() 方法返回一个 ReadableStream,在被读取时可以返回 Blob 中包含的数据。

File 和 FileReader

File

File 对象继承自 Blob,并扩展了与文件系统相关的功能。
有两种方式可以获取它。
第一种,与 Blob 类似,有一个构造器:
new File(fileParts, fileName, [options])
  • fileParts —— Blob/BufferSource/String 类型值的数组。
  • fileName —— 文件名字符串。
  • options —— 可选对象:
    • lastModified —— 最后一次修改的时间戳(整数日期)。
第二种,更常见的是,我们从 <input type="file"> 或拖放或其他浏览器接口来获取文件。在这种情况下,file 将从操作系统(OS)获得 this 信息。
由于 File 是继承自 Blob 的,所以 File 对象具有相同的属性,附加:
  • name —— 文件名,
  • lastModified —— 最后一次修改的时间戳。

FileReader

FileReader 是一个对象,其唯一目的是从 Blob(因此也从 File)对象中读取数据。
它使用事件来传递数据,因为从磁盘读取数据可能比较费时间。
主要方法:
  • readAsArrayBuffer(blob) —— 将数据读取为二进制格式的 ArrayBuffer
  • readAsText(blob, [encoding]) —— 将数据读取为给定编码(默认为 utf-8 编码)的文本字符串。
  • readAsDataURL(blob) —— 读取二进制数据,并将其编码为 base64 的 data url。
  • abort() —— 取消操作。
read* 方法的选择,取决于我们喜欢哪种格式,以及如何使用数据。
  • 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. 二进制数据,文件 (javascript.info)
上一篇
Tools Recommended —— Acid Tabs
下一篇
现代 JavaScript —— 杂项(Frame 和 window)

评论
Loading...