文章目录
- 1 背景
- 2 buffer拼接
- 3 buffer 截取
1 背景
最近有一个需求需要将缩略图和视频文件合并到一起用HTTP POST 发送给服务器,服务器解析后拆成缩略图和视频文件存储到云存储中。
于是就写了下面这段代码读了读取两个文件,并相加。就实现下面这段代码。
const fs = require('fs');
const http = require('http');
const thumbnail = fs.readFileSync('./20190306_00160734.jpg');
const video = fs.readFileSync('./20190306_00160734.MOV');
const bodyData = thumbnail + video;// thumbnail + video;
console.info(`thumbnail length :${thumbnail.length}`);
console.info(`video length :${video.length}`);
console.info(`video+thumbnail length:${ video.length+thumbnail.length}`);
console.info(`result length :${bodyData.length}`);
输出:
thumbnail length :103143
video length :1193998
video+thumbnail length:1297141
result length :1231302
结果发现两个buffer相加后的变量大小比两个变量大小的和更大, 黑人问号???
后面一想, fs.readFileSync 这东西读出来的东西应该不是string 。 这东西是一个buffer。
再一想, 依稀想到深入浅出nodejs里面说过buffer不能拿着就开加,而是有相应API(buffer.concat)去拼接。
而如果直接用这个去做 加法 会发生什么事呢?
Buffer1+Buffer2
两个buffer 相加实质上是两个buffer 转成string 相加。
Buffer1.toString()+Buffer2.toString()
而为什么toString会出现这个问题呢?实质上归根接地还是toString 编码的原因造成:
toString 行为默认编码是UTF-8格式。而readFile 默认是没有编码的。
const fs = require('fs');
const http = require('http');
const thumbnail = fs.readFileSync('./20190306_00160734.jpg');
const video = fs.readFileSync('./20190306_00160734.MOV');
console.info(`thumbnail length :${thumbnail.length}`);
console.info(`thumbnail string length:${thumbnail.toString().length}`);
console.info(`video length :${video.length}`);
console.info(`video string length :${video.toString().length}`);
输出
thumbnail length :103143
thumbnail string length:99410
video length :1193998
video string length :1131892
可以很明显看到 Buffer toString(对2进制数据UTF8编码后) 后length都发生了变化。数据长度变小了 。最终导致结果不对。
2 buffer拼接
Nodejs 提供了下面这个API 进行拼接
Buffer.concat(list[, totalLength])
list <Buffer[]> | <Uint8Array[]> 要合并的 Buffer 数组或 Uint8Array 数组。
totalLength <integer> 合并后 list 中的 Buffer 实例的总长度。
返回: <Buffer>
sample:
const buf1 = Buffer.alloc(10);
const buf2 = Buffer.alloc(14);
const buf3 = Buffer.alloc(18);
const totalLength = buf1.length + buf2.length + buf3.length;
console.log(totalLength);
// Prints: 42
const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);
console.log(bufA);
// Prints: <Buffer 00 00 00 00 ...>
console.log(bufA.length);
// Prints: 42
使用上诉API,对缩略图和video文件进行拼接后数据大小就正常了。
const fs = require('fs');
const http = require('http');
const thumbnail = fs.readFileSync('./20190306_00160734.jpg');
const video = fs.readFileSync('./20190306_00160734.MOV');
const bodyData1 = Buffer.concat([thumbnail, video], video.length+thumbnail.length);
console.info(`thumbnail length :${thumbnail.length}`);
console.info(`video length :${video.length}`);
console.info(`thumbnail+video length:${thumbnail.length + video.length}`);
console.info(`concat length :${bodyData1.length}`);
输出
thumbnail length :103143
video length :1193998
thumbnail+video length:1297141
concat length :1297141
3 buffer 截取
这里拼接成功后,发给服务器,服务器实质上也要进行buffer截取。提取出对应缩略图和video 调用的是下面的API:
Buffer.from(arrayBuffer[, byteOffset[, length]])
arrayBuffer <ArrayBuffer> | <SharedArrayBuffer> 一个 ArrayBuffer、SharedArrayBuffer、或 TypedArray 的 .buffer 属性。
byteOffset <integer> 开始拷贝的索引。默认值: 0。
length <integer> 拷贝的字节数。默认值: arrayBuffer.length - byteOffset。
const ab = new ArrayBuffer(10);
const buf = Buffer.from(ab, 0, 2);
console.log(buf.length);
实例
比如通过下面方式接受HTTP Body 中内容.
下面代码分段接受数据 通过,数组方式暂存各段数据,再使用concat 拼接数据。
这里比较关键的是Buffer.from(arrayBuffer[, byteOffset[, length]]) API 接受参数是arrayBuffer。
concat API 返回的是buffer, 所以这里要用 Buffer.buffer 把Buffer转换成ArrayBuffer。
let trunks = [];
let trunksLength = 0;
req.on('data',function(trunk){
trunks.push(trunk); //Data: video + thumbnail Size: video.length
trunksLength+=trunk.length;
}).on('end',function(){
logger.info(trunksLength);
const uploadFileData = Buffer.concat(trunks, trunksLength);
const viodeBuffer = Buffer.from(uploadFileData.buffer, 0, req.headers.size);
const thumbnailBuffer = Buffer.from(uploadFileData.buffer, req.headers.size);
});