流与二进制

5/19/2022 node.js

# 流与二进制

# Buffer

Buffer只存在于Node环境中,用来表示字节序列,通常当我们使用fs模块读取文件时就可以拿到文件所对应的字节序列(或者说是在内存中的值)。当响应的内容为Buffer时,响应头Content-Type的值为application/octet-stream

# 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>
1
2

# Buffer.from()

可以用来获取字符串对应的字节序列(默认UTF8编码)

const buffer = Buffer.from('aka') // <Buffer 61 6b 61>  
const buffer = Buffer.from('你好') // <Buffer e4 bd a0 e5 a5 bd>
1
2

# buffer.toString()

buffer.from()的逆操作,获取字节序列对应的字符串(默认UTF8编码)

const buffer = Buffer.from('aka') 
const str = buffer.toString() // aka
1
2

# Blob

Blob只存在于浏览器环境中,可以大致把它视为一个类文件对象(FileBlob的子类)。通常当我们使用fetch发送请求,后端返回Buffer,我们可以使用res.blob()拿到blob

fetch()
.then(res => res.blob())
.then(blob => {
    console.log(blob)
})
1
2
3
4
5

# blob.arrayBuffer()

可以用来将blob转化为arrayBuffer,从而可以看到对应的字节序列。

# blob.text()

可以用来获取blob内部字节序列所对应的文本。

blob.text().then(text => console.log(text))
1

# URL.createObjectURL(blob)

当后端将读取的文件作为响应体发送给前端时,我们常见的需求有:①下载文件到本地。②在本地显示图片。

这种需求的关键在于根据给定的文件或blob生成一个URL路径,将其放入aimg标签的属性中。

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
1
2
3
4
5
6

# blob.slice()

将文件或blob分割成多个blob,常用于大型图片的上传。

# File

FileBlob的子类,通常我们会在<input type="file">中获取到file对象。

const el = document.querySelector('input')
console.log(el.files) // FileList
el.files[0] // File
1
2
3

# FileReader

blob自带了一些方法来获取二进制、文本、URL。而我们也可以使用FileReader来实现类似的功能。

const reader = new FileReader()
reader.onload = function() {
    console.log(reader.result)
}
reader.readAsXXX(blob)
1
2
3
4
5

# readAsArrayBuffer(blob)

类似blob.arrayBuffer()

# readAsText(blob)

类似blob.text()

# readAsDataURL(blob)

用法类似URL.createObjectURL,但实际上两者还是有点不一样。

这个方式是将文件内容的字节序列使用Base64编码得到字符串,即以data:application/octet-stream;base64,开头的URL;而URL.createObjectURL()拿到的是一个以blob:http://xxx.com/xxx开头的URL。

# ArrayBuffer

简单来说ArrayBuffer是一块内存,但是我们不能直接去操作这块内存,必须通过Typed Array来操作这块内存,Typed ArrayInt8ArrayUnt8ArrayInt16Array等等。

# stream

流对象用来处理Node中的流式数据,Stream继承于eventEmitter,因此实例存在on方法,除此之外流对象的实例存在pipe方法来实现流式数据的传输。

流对象有四种类型:

  • Writable,比如fs.createWriteStreamprocess.stdoutres
  • Readable,比如fs.createReadStreamprocess.stdinreq
  • Duplex,可以当成WritableReadable的结合
  • Transform

# Writable

对于Writable类型的流对象,实例存在write()end()方法。

# Readable

对于Readable类型的流对象,实例存在on('data')on('end')方法。

# pipeline

const { stdout, stdin } = require('process')
const { pipeline } = require('stream')

// 写法一
stdin.on('data', chunk => {
    stdout.write(chunk)
})

// 写法二
stdin.pipe(stdout)

// 写法三
pipeline(stdin, stdout)
1
2
3
4
5
6
7
8
9
10
11
12
13

# zlib

Node的Zlib模块提供了基于Stream的API来实现gzip等格式的压缩或解压缩。

const zlib = require('zlib')
const { pipeline } = require('stream')
const gzip = zlib.createGzip() // 创建一个流对象
const source = fs.createReadStream('./a.txt')
const target = fs.createWriteStream('./a.txt.gz')

pipeline(source, gzip, target, err => console.log(err)) // 生成压缩文件a.txt.gz
1
2
3
4
5
6
7

当然我们也可以使用基于回调函数的写法来直接对文件进行压缩,经过测试可以把160Kb的页面压缩至4Kb,当浏览器识别到响应头部的Content-Encoding: gzip,浏览器就会自动对响应的内容进行解压缩

// server.js
const { promisify } = require('util')
const fs = require('fs')
const zlib = require('zlib')
const readFile = promisify(fs.readFile)
const gzip = promisify(zlib.gzip)
const http = require('http')

http.createServer(async (req, res) => {
    if (req.url === '/') {
        const page = await readFile('./index.html')
        const data = await gzip(page)
        res.setHeader('Content-Encoding', 'gzip')
        res.end(data)
    } 
    else if (req.url === '/test') {
        let obj = {}
        for (let i = 0; i < 10000; i++) {
            obj[i] = i
        }
        const data = await gzip(JSON.stringify(obj))
        res.setHeader('Content-Encoding', 'gzip')
        res.end(data)
    }
}).listen(3000)

// client.js
fetch('/test')
.then(res => res.json())
.then(data => console.log(data)) // 拿到obj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Last Updated: 9/7/2022, 11:32:53 PM