本文記錄下最近遇到的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