Node.js HTTP请求:如何检测响应正文的编码?

4

我正在使用https.request()通过以下常见模式进行HTTPS请求:

var request = https.request(options, function (response) {
    var chunks = [];
    response.on('data', function (chunk) {
        chunks.push(chunk);
    });

    response.on('end', function () {
        var buffer = Buffer.concat(chunks);
        ...
    });
});
...
request.end();
...

一旦我有了完成的响应Buffer,它需要打包成一个JSON对象。原因是我正在创建一种隧道,通过该隧道,HTTP响应(其标头、状态和主体)将作为JSON发送到另一个协议。
为了支持文本和二进制响应,目前对我有效的方法是将Buffer编码为Base64(使用buffer.toString('base64')),并在另一端使用new Buffer(theJsonObject.body, 'base64')解码它。虽然这可以工作,但如果我只在已知HTTP请求响应是二进制类型(例如图像)时选择性地执行Base64编码,那么效率会更高。否则,在上面显示的https.request()回调中,我可以简单地使用chunk.toString(),并将响应主体以UTF-8字符串类型传递到JSON对象中。我的JSON对象可能包含一个额外的属性,指示隧道的另一端“body”是UTF-8字符串(例如.htm、.css等)还是Base64编码(例如图像)。
我可以尝试使用响应content-type标头中的MIME类型来确定响应是否为二进制。我可能会维护一个白名单,其中包括我知道可以安全地假设为UTF-8的类型(如"text/html"等)。所有其他类型(包括例如'image/png')都将被Base64编码。
有人能提出更好的解决方案吗?

1
我有点迷惑。为什么你不能使用“Content-Type”头,不用担心所有这些东西呢? - freakish
嗨@freakish,问题在于我想以最大小效率的方式将响应内容打包到JSON对象中,因此我需要编程确定它是否应视为二进制数据。如果我仅将其视为字符串(因此在“数据”回调中使用buffer.toString()),则由于编码而会损坏图像二进制数据。我可以对所有内容进行Base64编码(我的演示现在正在做这件事),但这会不必要地膨胀文本响应(例如html、css、js)。 - Trevor
那么使用 Content-Type 可能真的是唯一的方法。想想看,如果我可以安全地检测到该标头值中出现了 "text",那么我是否可以假定它是文本数据呢?如果我没有在值中看到 "text",那么只需进行 Base64 编码即可?也许这将是一个安全的策略?(我还应该提到,我完全控制所访问的端点服务器。因此,我可以保证 MIME 类型是正确的。) - Trevor
1
嗯,isTextOrBinary - adeneo
总结:我建议您将响应作为HTTP响应代理,就像您收到的一样。如果您想添加一些元数据,您可以始终定义自定义标头。否则,您正在重新发明轮子。 - freakish
显示剩余9条评论
1个回答

1

你可以使用file-type包通过检查缓冲区的魔数来检测文件类型。

安装

npm install --save file-type

使用方法

var fileType = require('file-type');
var safeTypes = ['image/gif'];
var request = https.request(options, function (response) {
    var chunks = [];
    response.on('data', function (chunk) {
        chunks.push(chunk);
    });

    response.on('end', function () {
        var buffer = Buffer.concat(chunks);
        var file = fileType(buffer) );
        console.log( file );
        //=> { ext: 'gif', mime: 'image/gif' } 

        // mime isn't safe
        if ( safeTypes.indexOf(file.mime) == '-1' ) {
            // do your Base64 thing
        }
    });
});
...
request.end();
...

如果您希望保持代码包的免费,可以查看Github上的软件包源代码,它非常简洁。

我有与提问者完全相同的用例,这个答案非常有效。它给了我缓冲区内容的MIME类型!这是一种很好的方法,可以双重检查缓冲区实际上是文本还是你在“content-type”头中告诉它的类型,否则你可能会将二进制数据视为字符串。感谢Kevin Leary! - chwagssd

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