本文记录下最近遇到的buffer拼接问题。如果需要知道如何拼接 直接点击第二节
最近有一个需求需要将缩略图和视频文件合并到一起用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都发生了变化。数据长度变小了 。最终导致结果不对。
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:
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
这里拼接成功后,发给服务器,服务器实质上也要进行buffer截取。提取出对应缩略图和video 调用的是下面的API:
buffur.slice API:
buf.slice([start[, end]])
start 新的Buffer开始位置。默认值: 0。
end 新的Buffer终止处(不包括在内)。 默认值: buf.length。
返回:
返回一个新Buffer引用,该引用与原始引用相同的内存,但由start和end索引偏移并裁剪。
测试代码
var fs = require('fs');
const videoFile = fs.readFileSync('./20190306_00160734.MOV');
var video = Buffer.from(videoFile);
console.info(video.length);
var video1 = video.slice(0, 500000);
var video2 = video.slice(500000, 1000000);
var video3 = video.slice(1000000);
console.log(video1.length);
console.log(video2.length);
console.log(video3.length);
const videoAll = Buffer.concat([video1, video2, video3], video1.length + video2.length + video3.length);
fs.writeFileSync('./test.MOV', videoAll);
// 1193998
// 500000
// 500000
// 193998