本文记录下最近遇到的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:
buffur.slice API:

buf.slice([start[, end]])
start <integer>新的Buffer开始位置。默认值: 0。
end <integer>新的Buffer终止处(不包括在内)。 默认值: buf.length。
返回:<Buffer>
返回一个新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


Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐