Node.js/Express和二进制数据在POST请求中的应用

10

我试图向一个Express应用程序发送二进制数据。只要我的值小于0x80,它就可以正常工作。如果单个值为0x80或更大,则会破坏整个缓冲区。

Express处理程序:

binary = require('binary');

exports.api = function(req, res){
    var body = req.body.name;
    var buf = new Buffer(body,'binary');

    console.log('body',req.body);
    console.log('req body len', body.length);
    console.log('buf len', buf.length);

    var g = binary.parse(buf)
        .word16bu('a') // unsigned 16-bit big-endian value
        .word16bu('b').vars

    console.log('g.a', g.a);
    console.log('g.b', g.b);

  res.send("respond with a resource");
};

Python客户端(Content-Type: application/x-www-form-urlencoded):

import requests
from struct import pack
# send two unsigned shorts (16-bits each).
requests.post('http://localhost:3000/api', data={'name':pack('!HH',1,2)})

当数据等于1、2时,Express输出。这正是我所期望的。

body { name: '\u0000\u0001\u0000\u0002' }
req body len 4
buf len 4
g.a 1
g.b 2
POST /api 200 1ms - 23b

当数据等于1、0xFF时,输出Express。有趣的是,9520实际上是十六进制中的0x25 0x30,对应于ASCII中的“%0”。是的,它似乎正在解析“%00%01……”字符串。我希望我知道如何防止这种情况!!!

body { name: '%00%01%00%FF' }
req body len 12
buf len 12
g.a 9520
g.b 12325
POST /api 200 2ms - 23b

如何使用Multer上传二进制文件:https://www.youtube.com/watch?v=d8COHTGz2cc - Inzamam Malik
4个回答

8

在苦苦挣扎了很长时间后,我想出了这个解决方案:

var express = require('express');
var bodyParser = require('body-parser');
var fs = require('fs');
var path = require('path');

app.use(bodyParser.raw({type: 'application/octet-stream', limit : '2mb'}))

app.post('/nist-ws/rest/v1/nist/', function(req, res) {
    var filePath = path.join(__dirname, 'nist_received', `/${Date.now()}.nist`)
    fs.open(filePath, 'w', function(err, fd) {  
        fs.write(fd, req.body, 0, req.body.length, null, function(err) {
            if (err) throw 'error writing file: ' + err;
            fs.close(fd, function() {
                console.log('wrote the file successfully');
                res.status(200).end();
            });
        });
    });
});

bodyParser.raw会将请求体填充为一个Buffer,其余代码来自:https://stackabuse.com/writing-to-files-in-node-js/


1
补充一下Carl的回答。当我通过表单接收文件时,这对我没有用。为了解决这个问题,我将他的bodyParser.raw行更改为: app.use(bodyParser.raw({type: 'multipart/form-data', limit : '2mb'}))因为在发送表单时,这是Content-Type - cs94njw

7

结果证明这是一个编码问题。一切都似乎默认为'utf8',但在这种情况下需要设置为'binary'。

例如:

var buf = new Buffer(body.toString('binary'),'binary');

同样重要的是,我需要将nodejs multiparty解析器的编码设置为“二进制”。请参见:https://github.com/superjoe30/node-multiparty/issues/37

1
我出现了“req.body.toString不是一个函数”的错误,尽管我按照你的方式声明了body。你知道这可能是从哪里来的吗? - Hey

2
一种现代的 TypeScript 安全方法可能是这样的:
export async function readBodyAsBuffer(req: any): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    let buffer = Buffer.alloc(0)
    req.setEncoding(null)
    req.on(
      "data",
      (chunk: string) => (buffer = Buffer.concat([buffer, Buffer.from(chunk)]))
    )
    req.on("end", () => resolve(buffer))
    req.on("error", reject)
  })
}

现在您可以像这样简单地获取Buffer的正文内容:
let buffer = await readBodyAsBuffer(req)

Buffer.concat 在这种用法下对于大文件的性能非常糟糕。最好将数据块收集到一个数组中,并在“结束”时仅对该数组执行一次 Buffer.concat,这样会更加高效。 - Marko Kohtala
那是一个很好的提示。我同意,那很可能会提高性能。 - Holtwick

0
与Holtwick类似,这是我正在使用的:
const readBodyAsBuffer = (req: express.Request): Promise<Buffer> => {
  return new Promise((resolve, reject) => {
    let body: Buffer[] = []
    req.on('data', chunk => {
      body.push(chunk)
    })
    req.on('end', () => {
      resolve(Buffer.concat(body))
    })
    req.on('error', err => {
      reject(err)
    })
  })
}

使用 const buffer = await readBodyAsBuffer(req) 作为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接