【開發筆記】Node.js 處理 POST Body Data 的幾個細節
最近處理 Exif 解析一個運用上的細節,後來的解決方案就是利用 POST Body 來傳送 JavaScript 的 ArrayBuffer。本來以為是很直接的東西,沒想到實作起來還是有些坑需要留意...
原生 HTTP 請求取數據
因為原生 HTTP POST 請求的 Body 本來就是 Buffer,所以搜集 Body 的 Data Chunk 本來是很直覺的做法。但是實作起來,發現 HTTP 請求的 end 事件並非總是觸發。究其原因,發現原來在出請求附帶的 Post Body 中,我只是發送整個 Data Buffer 最前端 128 KB 的部分。因此接收的服務端會一直閒置等待,直到 Gateway 超時,拋出 HTTP 504 狀態碼。雖然可以從檢查 Body Data Buffer 的長度來判斷結束的時間,但是假定本身不該是固定長度的數據顯然不是個好主意。
let body = [];
req.on('data', (chunk) => {
// 或許可以檢查 body 的長度來判斷何時給予客戶端 response
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// 如果數據完整傳完的話,應該會觸發 end 事件
});
採用 Node.js + Express.js 的 Middleware
採用 Express.js 框架的好處,就是可以透過 Middleware 來處理 Post Body。差別就是 middleware 處理過的請求,我們可以直接從 req.body 取得我們要的整段 Buffer。而非如前述原生 HTTP 請求的方式,自己監聽 data 和 end 事件來拼接 Buffer。不過,因為筆者應用的數據解析比較特別,所以另自訂了 Content-Type: application/exif.buffer
來跟其他的內容類型(如: application/json
)有個區隔。
// 可以在 root class 做個通用設置
const option = {
inflate: true,
limit: '100mb',
type: 'application/exif.buffer'
};
app.use(express.raw(option));
// 也可以在個別的路由下分別設置
const option = {
inflate: true,
limit: '100mb',
type: 'application/exif.buffer'
};
router.post('/lens/:Make',
express.raw(option),
async (req, res) => {
...
});
雖然同樣是 413 狀態碼
其次就是 413 狀態碼拋出有兩個地方。一個是 Node.js + Express.js 本身,HTTP 請求返回的顯示是 413 Payload Too Large
。解決的方法參考前述,就是在 middleware 的設定將 limit 的限制放寬。另外就是 Nginx 也會拋出 413 狀態碼,這個時候顯示的將會是 ERR_FAILED 413 Request Entity Too Large
。至於處理上,只要在 /etc/nginx/nginx.conf 加上 client_max_body_size
設置放寬容許量即可。
Byte Order 不一致
最後就是遠端的檔案,一開始是發現 Canon 家的檔案會有部分 Exif 標籤遺失,而 Nikon 家的不會。接著才找出是因為 FileReader 讀取出來的 ArrayBuffer 和 fs 讀取出來的 ArrayBuffer,轉成 UInt8Array 後,竟然裡面的 Byte Order 沒有完全一樣。後來比較起來,是 fs 的版本才能完整保留 Canon 家的 Exif 標籤(翻桌)。