我想从https请求中检索二进制数据。
我找到了一个使用请求方法的类似问题, Getting binary content in Node.js using request,是说将编码设置为 null 应该有效,但事实并非如此。
options = {
hostname: urloptions.hostname,
path: urloptions.path,
method: 'GET',
rejectUnauthorized: false,
encoding: null
};
req = https.request(options, function(res) {
var data;
data = "";
res.on('data', function(chunk) {
return data += chunk;
});
res.on('end', function() {
return loadFile(data);
});
res.on('error', function(err) {
console.log("Error during HTTP request");
console.log(err.message);
});
})
修改:将编码设置为'二进制'无效
答案 0 :(得分:55)
接受的答案对我不起作用(即将编码设置为二进制),即使是提出问题的用户也无效。
这对我有用,取自:http://chad.pantherdev.com/node-js-binary-http-streams/
http.get(url.parse('http://myserver.com:9999/package'), function(res) {
var data = [];
res.on('data', function(chunk) {
data.push(chunk);
}).on('end', function() {
//at this point data is an array of Buffers
//so Buffer.concat() can make us a new Buffer
//of all of them together
var buffer = Buffer.concat(data);
console.log(buffer.toString('base64'));
});
});
修改:按照Semicolon的建议更新答案
答案 1 :(得分:15)
您需要将编码设置为响应,而不是请求:
req = https.request(options, function(res) {
res.setEncoding('binary');
var data = [ ];
res.on('data', function(chunk) {
data.push(chunk);
});
res.on('end', function() {
var binary = Buffer.concat(data);
// binary is your data
});
res.on('error', function(err) {
console.log("Error during HTTP request");
console.log(err.message);
});
});
这是一个有用的答案:Writing image to local server
答案 2 :(得分:8)
在AWS Lambda环境中的NodeJS 6.10(以及2019年2月测试的8.10)上运行,对我来说,没有任何解决方案。
对我有用的是以下内容:
https.get(opt, (res) => {
res.setEncoding('binary');
let chunks = [];
res.on('data', (chunk) => {
chunks.push(Buffer.from(chunk, 'binary'));
});
res.on('end', () => {
let binary = Buffer.concat(chunks);
// binary is now a Buffer that can be used as Uint8Array or as
// any other TypedArray for data processing in NodeJS or
// passed on via the Buffer to something else.
});
});
记下res.setEncoding(' binary');和Buffer.from(chunk,' binary')行。一个设置响应编码,另一个从前面指定的编码中提供的字符串创建一个Buffer对象。
答案 3 :(得分:2)
这里的每个人都在正确的轨道上,但是要解决这个问题,您不能致电.setEncoding()
永远。
如果您呼叫.setEncoding()
,它将创建一个StringDecoder
并将其设置为default decoder。如果尝试传递null
或undefined
,则它仍将创建一个StringDecoder
,其默认解码器为UTF-8
。即使您呼叫.setEncoding('binary')
,也与呼叫.setEncoding('latin1')
相同。是的,seriously。
我希望我可以说您将._readableState.encoding
和_readableState.decoder
设置回null
,但是当您调用.setEncoding()
时,缓冲区被擦除并替换为解码后的二进制编码一连串的东西。这意味着您的数据已被更改。
如果要“撤消”解码,则必须将数据流重新编码回二进制,如下所示:
req.on('data', (chunk) => {
let buffer;
if (typeof chunk === 'string') {
buffer = Buffer.from(chunk, req.readableEncoding);
} else {
buffer = chunk;
}
// Handle chunk
});
当然,如果您从不调用.setEncoding()
,则不必担心将块作为string
返回。
将块命名为Buffer
之后,就可以根据需要使用它了。为了全面,这里介绍了如何使用预设的缓冲区大小,同时还要检查Content-Length
:
const BUFFER_SIZE = 4096;
/**
* @param {IncomingMessage} req
* @return {Promise<Buffer>}
*/
function readEntireRequest(req) {
return new Promise((resolve, reject) => {
const expectedSize = parseInt(req.headers['content-length'], 10) || null;
let data = Buffer.alloc(Math.min(BUFFER_SIZE, expectedSize || BUFFER_SIZE));
let bytesWritten = 0;
req.on('data', (chunk) => {
if ((chunk.length + bytesWritten) > data.length) {
// Buffer is too small. Double it.
let newLength = data.length * 2;
while (newLength < chunk.length + data.length) {
newLength *= 2;
}
const newBuffer = Buffer.alloc(newLength);
data.copy(newBuffer);
data = newBuffer;
}
bytesWritten += chunk.copy(data, bytesWritten);
if (bytesWritten === expectedSize) {
// If we trust Content-Length, we could return immediately here.
}
});
req.on('end', () => {
if (data.length > bytesWritten) {
// Return a slice of the original buffer
data = data.subarray(0, bytesWritten);
}
resolve(data);
});
req.on('error', (err) => {
reject(err);
});
});
}
此处使用缓冲区大小的选择是避免立即保留大量内存,而是仅根据需要获取RAM。 Promise
功能只是为了方便。
答案 4 :(得分:1)
约翰·约翰逊(PärtJohanson),我希望我能发表评论,只为了感谢您将我从递归循环中解救出来,我整天都在梳理头发,然后一遍又一遍地阅读(极其无用的)节点文档。找到您的答案后,我去研究了文档,甚至找不到任何地方记录的res.setEncoding
方法!它只是作为两个示例的一部分显示的,其中两个示例称为res.setEncoding('utf8');
,您是在哪里找到的,或者是如何找到的呢!?
由于我没有足够的声誉来发表评论,所以我至少会对我的回答做出一些贡献:PärtJohanson的回答对我有100%的作用,我只是根据自己的需要对它进行了一些调整,因为我正在使用它在NWJS 0.36.4 /节点11.11.0上使用nw.Window.get().evalNWBin()
下载并评估服务器上托管的脚本(并使用nwjc编译)的脚本:
let opt = {...};
let req = require('https').request(opt, (res) => {
// server error returned
if (200 !== res.statusCode) {
res.setEncoding('utf8');
let data = '';
res.on('data', (strData) => {
data += strData;
});
res.on('end', () => {
if (!res.complete) {
console.log('Server error, incomplete response: ' + data);
} else {
console.log('Server error, response: ' + data);
}
});
}
// expected response
else {
res.setEncoding('binary');
let data = [];
res.on('data', (binData) => {
data.push(Buffer.from(binData, 'binary'));
});
res.on('end', () => {
data = Buffer.concat(data);
if (!res.complete) {
console.log('Request completed, incomplete response, ' + data.length + ' bytes received);
} else {
console.log('Request completed, ' + data.length + ' bytes received');
nw.Window.get().evalNWBin(null, data);
}
});
}
};
编辑:PS我发布此邮件是为了防止有人想知道如何处理非二进制响应-我的实际代码更深入一点,并检查响应内容类型标头以解析JSON(预期失败,即400、401、403)或HTML(意外失败,例如404或500)
答案 5 :(得分:0)
setEncoding()
方法,因为by default, no encoding is assigned and stream data will be returned as Buffer
objects Buffer.from()
回调方法中调用on.data
,将chunk
的值转换为Buffer
对象。http.get('my_url', (response) => {
const chunks = [];
response.on('data', chunk => chunks.push(Buffer.from(chunk))) // Converte `chunk` to a `Buffer` object.
.on('end', () => {
const buffer = Buffer.concat(chunks);
console.log(buffer.toString('base64'));
});
});
答案 6 :(得分:0)
和这里的其他人一样,我需要处理来自Node.js HTTP响应(又名http.IncomingMessage
)的二进制数据块。
除了PärtJohanson的answer及其变体之外,现有答案中的任何一个都对我的Electron 6项目(在发布时捆绑了Node.js 12.4.0)没有真正的作用。
即使使用该解决方案,这些块仍总是作为response.on('data', ondata)
对象(而不是期望的string
对象)到达Buffer
处理程序。 Buffer.from(chunk, 'binary')
带来了额外的转化。无论使用response.setEncoding('binary')
还是response.setEncoding(null)
显式指定二进制编码,我都会得到字符串。
我设法获取原始Buffer
块的唯一方法是将response
用管道传输到stream.Writable
的实例,在该实例中我提供了自定义的write
方法:
const https = require('https');
const { Writable } = require('stream');
async function getBinaryDataAsync(url) {
// start HTTP request, get binary response
const { request, response } = await new Promise((resolve, reject) => {
const request = https.request(url, {
method: 'GET',
headers: {
'Accept': 'application/pdf',
'Accept-Encoding': 'identity'
}
}
);
request.on('response', response =>
resolve({request, response}));
request.on('error', reject);
request.end();
});
// read the binary response by piping it to stream.Writable
const buffers = await new Promise((resolve, reject) => {
response.on('aborted', reject);
response.on('error', reject);
const chunks = [];
const stream = new Writable({
write: (chunk, encoding, notifyComplete) => {
try {
chunks.push(chunk);
notifyComplete();
}
catch(error) {
notifyComplete(error);
}
}
});
stream.on('error', reject);
stream.on('finish', () => resolve(chunks));
response.pipe(stream);
});
const buffer = Buffer.concat(buffers);
return buffer.buffer; // as ArrayBuffer
}
async function main() {
const arrayBuff = await getBinaryDataAsync('https://download.microsoft.com/download/8/A/4/8A48E46A-C355-4E5C-8417-E6ACD8A207D4/VisualStudioCode-TipsAndTricks-Vol.1.pdf');
console.log(arrayBuff.byteLength);
};
main().catch(error => console.error(error));
已更新,此行为仅在我们的Web API服务器中体现。因此,response.on('data')
实际上可以很好地用于我在上述代码段中使用的示例URL,因此不需要流。尽管这是特定于服务器的,但是这很奇怪,我正在进一步调查。