原文地址:https://yuzi.dev/posts/frontend/node-buffer-to-uintarray
在文章开始之前,感谢 GPT-4 给我的帮助和启发。
之前,下载的功能做好后,我就放着没管了,昨天下了个小文件(38B 大小)测试,发现下下来大小居然达到了 8KB!!!
源文件:
#!/bin/bash
java -jar server.jar nogui
下载下来的样子:
然后就开始了排错,难道是对称加密的问题?GPT 也给出了解释:PKCS7 的算法会有一个填充,可能是这个填充没去掉。但是我的WordArrayToArrayBuffer
函数确实是正确地处理了这个填充问题,另外一个佐证就是,上传小文件是没有任何问题的,只有下载有问题。这么说问题就不是出在对称加密上。
再审视一下 koa 端下载功能的代码实现:
const readable = fs.createReadStream(path, {
start: current,
end,
})
const buffer = await readStreamToBuffer(readable)
ctx.body = {
contentBuffer: buffer.buffer,
}
其中,buffer 的类型是 Node 的 Buffer,而其属性 buffer 则是提取出其底层的 ArrayBuffer。
将它们分别打印一下,果然就出现了问题:
console.log(buffer.toString())
console.log(new TextDecoder('utf-8').decode(buffer.buffer))
第一行打印出来的是正常的,第二行就出现各种奇奇怪怪的内容和乱码了!
查阅后得知:
在 Node.js 中,Buffer 对象是用于处理二进制数据的,它是 Uint8Array 的一个子类,但有一些额外的方法和属性。而 ArrayBuffer 是 JavaScript 的内建类型,用于处理二进制数据,它是 TypedArray 和 DataView 的基础。
在之前的代码中,我试图将 Buffer 对象的.buffer 属性赋值给 contentBuffer。这在某些情况下可能会导致问题,因为 Buffer 对象的.buffer 属性实际上是底层的 ArrayBuffer,它可能比 Buffer 对象本身更大。这是因为 Node.js 可能会为 Buffer 对象预分配更多的内存,以便在需要时追加更多的数据。
当试图将 Buffer 对象转换为 Uint8Array 时,你应该使用 Buffer 对象的.byteOffset 和.byteLength 属性,以确保只获取 Buffer 对象实际包含的数据,而不是整个底层的 ArrayBuffer。
const buffer = await readStreamToBuffer(readable);
const uint8Array = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
所以,原来是Node的Buffer转为原生的ArrayBuffer时我的理解出现了偏差,所以才出现了乱码。将下载部分按上述措施修改回来后,下载下来的小文件就没有任何问题了。看来对于各种API的用法、属性还是要多了解透啊!