Buffer
Buffer用于表示固定长度的字节序列,Buffer仅存在于Node.js环境中,在浏览器环境中使用ArrayBuffer作为替代,二者的原理基本一致但API层面存在些许差异。
通过fs模块不指定编码方式encoding读取文件时,我们能够获取到表示二进制数据的Buffer对象。
Buffer.alloc()
可以用来生成指定长度的Buffer,也可以同时指定Buffer字节序列的值。
const buffer = Buffer.alloc(10) // <Buffer 00 00 00 00 00 00 00 00 00 00>
const buffer = Buffer.alloc(10, 0xfc) // <Buffer fc fc fc fc fc fc fc fc fc fc>
Buffer.from(string[, encoding])
将字符串编码成Buffer表示的字节序列,默认为utf-8编码
const buffer = Buffer.from('aka') // <Buffer 61 6b 61>
const buffer = Buffer.from('你好') // <Buffer e4 bd a0 e5 a5 bd>
buffer.toString([encoding])
将Buffer解码成字符串,默认为utf-8编码
const buffer = Buffer.from('aka')
const str = buffer.toString() // aka
ArrayBuffer
浏览器中的ArrayBuffer对应Node.js环境中的Buffer,都表示内存中的一块字节序列,但在浏览器中我们不能直接操作这块内存,而是必须通过Typed Array来操作,常见的Typed Array有Int8Array、Uint8Array、Int16Array等等。
类型化数组(Typed Array)
new Uint8Array([100, 200, 300])
DataView
TODO
String and Encoding
无论是Buffer还是ArrayBuffer,本质上都表示着内存中的二进制数据(或者叫字节序列),通过某种编码方式我们就能获取其对应的字符串。
常见的编码方式有ascii、latin1、utf-8、utf-16、gbk等等,需要注意平常所说的Unicode指的是一种字符集(字符与码点的映射),utf-8、utf-16、utf-32才是具体的编码方式。
:::tip
ascii编码的有效位数为7位,无法表示某些西欧字符,latin-1使用了ascii中没有直接用到的最高位来兼容ascii的同时表达更多的字符。
:::
如我们在Buffer一节中所介绍的,在Node.js环境中可以通过Buffer.from()和buffer.toString()实现Buffer和字符串的转化(即二进制数据和字符串的编码和解码)。而在浏览器中并不存在Buffer变量,为了实现ArrayBuffer和字符串的转化,我们需要借助TextEncoder和TextDecoder来实现。
TextEncoder和TextDecoder可以用来实现TypedArray和字符串的转化,而TypedArray可以通过.buffer属性访问对应的ArrayBuffer对象,所以间接地实现了我们的需求。
TextEncoder
const arr = new TextEncoder().encode('akara') // Uint8Array(5) [97, 107, 97, 114, 97, buffer: ArrayBuffer(5)]
const buffer = arr.buffer // ArrayBuffer(5)
TextDecoder
const arr = new Uint8Array([97, 107, 97, 114, 97])
new TextDecoder().decode(arr) // akara
:::caution
需要注意的是,为了遵循规范TextEncoder和TextDecoder只支持utf-8编码。
:::
encodeURI
encodeURI('你') // '%E4%BD%A0'
const buffer = Buffer.from('你')
console.log(buffer) // <Buffer e4 bd a0>
decodeURI
decodeURI('%E4%BD%A0') // 你
Base64
Base64也是一种编码方式,可以实现二进制字节序列和字符串的转化。
给定一个字节序列,每三个字节作为一大组共有24个比特,将这24个比特分为四个小组,则每个小组包含6个比特,在每个小组前面加上2个比特00。那么原本3个字节的数据就变成了4个字节的数据,现在每个字节的有效位数为6位,此时每个字节可以映射为64种不同的字符,因此这种编码方式被叫做Base64。
但在实际生活中,我们会经常看到Base64来用来对字符串混淆(伪加密),明明这是一种针对二进制数据的编码方式?比如window.btoa,这是因为内部会先通过latin-1将字符串编码成二进制,再进行Base64编码,编码后的数据比原本增大了三分之一。
Base64的一个常用途径是用来将小型图片进行编码,通过<img src="data:img/gif;base64,base64,xxx" />形式进行图片的加载,从而减少不必要的网络请求。
Blob
Blob只存在于浏览器环境中,可以大致把它视为一个类文件对象,是后续将介绍的File对象的父类。
fetch()
.then(res => res.blob())
.then(blob => {
console.log(blob)
})
new Blob()
可用于根据ArrayBuffer或字符串生成Blob。
new Blob([JSON.stringify({
name: 'akara'
})], { type: 'application/json' })
new Blob([new Uint8Array([10, 20]).buffer], {})
blob.arrayBuffer()
将Blob转化为ArrayBuffer,需要注意函数的返回值为Promise
blob.text()
将Blob转化为字符串,编码为utf-8,需要注意函数的返回值为Promise
blob.text().then(text => console.log(text))
blob.slice()
将文件或blob分割成多个blob,常用于大型图片的上传。
URL.createObjectURL(blob)
当后端将读取的文件作为响应体发送给前端时,我们常见的需求有:①下载文件到本地。②在本地显示图片。
这种需求的关键在于根据给定的文件或blob生成一个URL路径,将其放入a或img标签的属性中。
const url = URL.createObjectURL(blob)
a.href = url // 结构类似于 blob:http://localhost:3000/486ef892-d4fc-485f-b4ab-fae272d35e55
a.download = '下载文件.txt' // 文件名
const url2 = URL.createObjectURL(blob)
img.src = url2
File
File是Blob的子类,通常进行文件上传时可以拿到File对象。
const el = document.querySelector('input')
console.log(el.files) // FileList
el.files[0] // File
闭环转换
通过上述几节我们了解到ArrayBuffer、String、Blob的基本情况,并且知道它们直接是可以进行闭环任意转化的:
String -> Uint8Array(TextEncoder)Uint8Array -> String(TextDecoder)Uint8Array -> ArrayBuffer(.buffer)ArrayBuffer -> Uint8Array(new Uint8Array)ArrayBuffer -> Blob(new Blob)Blob -> ArrayBuffer(arrayBuffer)Blob -> String(text)String -> Blob(new Blob)
FileReader
FileReader用于读取文件中的数据,Blob内置了一些方法来获取对应的ArrayBuffer、字符串,而FileReader也提供了类似的功能。
const reader = new FileReader()
reader.onload = function() {
console.log(reader.result)
}
reader.readAsXXX(blob)
readAsArrayBuffer()
功能类似于blob.arrayBuffer()
readAsText()
功能类似于blob.text()
readAsDataURL()
功能类似于URL.createObjectURL,但稍微有些不同。
这个方式是将文件内容的字节序列通过Base64编码得到字符串,即以data:application/octet-stream;base64,开头的长URL;而URL.createObjectURL()实际上拿到的是一个以blob:http://xxx.com/xxx开头的短URL,这个URL将会指向内存中的对应地址。
readAsBinaryString()
感觉是通过类似latin-1来进行逐字节的字符串编码。