我正在入侵smtp-protocol
用于捕获SMTP电子邮件并处理邮件数据的Node程序。该库将邮件数据作为流提供,但我不知道如何将其转换为字符串。
我目前正在使用将其写入stdout stream.pipe(process.stdout, { end: false })
,但是正如我所说,我需要将流数据存储在字符串中,一旦流结束,就可以使用它。
如何将所有数据从Node.js流收集到字符串中?
我正在入侵smtp-protocol
用于捕获SMTP电子邮件并处理邮件数据的Node程序。该库将邮件数据作为流提供,但我不知道如何将其转换为字符串。
我目前正在使用将其写入stdout stream.pipe(process.stdout, { end: false })
,但是正如我所说,我需要将流数据存储在字符串中,一旦流结束,就可以使用它。
如何将所有数据从Node.js流收集到字符串中?
Answers:
(这个答案来自几年前的最佳答案。现在,下面有一个更好的答案。我没有跟上node.js的步伐,我无法删除此答案,因为它被标记为“在此问题上正确” “。如果您想点击一下鼠标,您想让我做什么?)
关键是使用Readable Stream的data
和end
事件。听这些事件:
stream.on('data', (chunk) => { ... });
stream.on('end', () => { ... });
收到data
事件后,将新的数据块添加到为收集数据而创建的缓冲区中。
收到end
事件时,如有必要,将完成的Buffer转换为字符串。然后做你需要做的。
另一种方法是将流转换为Promise(请参见下面的示例),然后使用then
(或await
)将解析后的值分配给变量。
function streamToString (stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on('data', chunk => chunks.push(chunk))
stream.on('error', reject)
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')))
})
}
const result = await streamToString(stream)
SyntaxError: await is only valid in async function
。我究竟做错了什么?
streamToString(stream).then(function(response){//Do whatever you want with response});
.toString("utf8")
在最后调用,以避免如果在多字节字符中间分割一个块而导致解码失败的问题;(2)实际错误处理;(3)将代码放入函数中,以便可以重用,而不是复制粘贴;(4)使用Promises,以便可以await
启用该功能;(5)与某些npm库不同,它不会拖曳一百万个依赖项的小代码;(6)ES6语法和现代最佳实践。
Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received type string
如果流生成的是string
块而不是,则上述代码可能会失败Buffer
。使用chunks.push(Buffer.from(chunk))
应该与string
和Buffer
块一起使用。
以上都不对我有用。我需要使用Buffer对象:
const chunks = [];
readStream.on("data", function (chunk) {
chunks.push(chunk);
});
// Send the buffer or you can put it into a var
readStream.on("end", function () {
res.send(Buffer.concat(chunks));
});
希望这比上面的答案更有用:
var string = '';
stream.on('data',function(data){
string += data.toString();
console.log('stream data ' + part);
});
stream.on('end',function(){
console.log('final output ' + string);
});
请注意,字符串串联不是收集字符串部分的最有效方法,但它是出于简化目的(也许您的代码并不关心效率)。
同样,此代码可能会为非ASCII文本产生无法预测的错误(它假定每个字符都适合一个字节),但是也许您也不在乎。
join("")
在最后调用该数组。
我通常使用这个简单的函数将流转换为字符串:
function streamToString(stream, cb) {
const chunks = [];
stream.on('data', (chunk) => {
chunks.push(chunk.toString());
});
stream.on('end', () => {
cb(chunks.join(''));
});
}
用法示例:
let stream = fs.createReadStream('./myFile.foo');
streamToString(stream, (data) => {
console.log(data); // data is now my string variable
});
chunks.push(chunk.toString());
还有一个使用诺言的字符串:
function getStream(stream) {
return new Promise(resolve => {
const chunks = [];
# Buffer.from is required if chunk is a String, see comments
stream.on("data", chunk => chunks.push(Buffer.from(chunk)));
stream.on("end", () => resolve(Buffer.concat(chunks).toString()));
});
}
用法:
const stream = fs.createReadStream(__filename);
getStream(stream).then(r=>console.log(r));
.toString()
如果需要,请删除以与二进制数据一起使用。
更新:@AndreiLED正确指出这存在字符串问题。我无法获得具有返回的节点版本的字符串的流,但是api指出这是可能的。
Uncaught TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received type string
如果流生成的是string
块而不是,则上面的代码可能会失败Buffer
。使用chunks.push(Buffer.from(chunk))
应该与string
和Buffer
块一起使用。
流没有简单的.toString()
功能(我理解),也没有类似.toStringAsync(cb)
函数的东西(我不理解)。
因此,我创建了自己的辅助函数:
var streamToString = function(stream, callback) {
var str = '';
stream.on('data', function(chunk) {
str += chunk;
});
stream.on('end', function() {
callback(str);
});
}
// how to use:
streamToString(myStream, function(myStr) {
console.log(myStr);
});
最干净的解决方案可能是使用“字符串流”包,该包将流转换为带有承诺的字符串。
const streamString = require('stream-string')
streamString(myStream).then(string_variable => {
// myStream was converted to a string, and that string is stored in string_variable
console.log(string_variable)
}).catch(err => {
// myStream emitted an error event (err), so the promise from stream-string was rejected
throw err
})
流行(每周下载超过500万)和轻量级get-stream库的简单方法:
https://www.npmjs.com/package/get-stream
const fs = require('fs');
const getStream = require('get-stream');
(async () => {
const stream = fs.createReadStream('unicorn.txt');
console.log(await getStream(stream)); //output is string
})();
诸如减速器之类的东西呢?
这是一个使用ES6类的示例。
var stream = require('stream')
class StreamReducer extends stream.Writable {
constructor(chunkReducer, initialvalue, cb) {
super();
this.reducer = chunkReducer;
this.accumulator = initialvalue;
this.cb = cb;
}
_write(chunk, enc, next) {
this.accumulator = this.reducer(this.accumulator, chunk);
next();
}
end() {
this.cb(null, this.accumulator)
}
}
// just a test stream
class EmitterStream extends stream.Readable {
constructor(chunks) {
super();
this.chunks = chunks;
}
_read() {
this.chunks.forEach(function (chunk) {
this.push(chunk);
}.bind(this));
this.push(null);
}
}
// just transform the strings into buffer as we would get from fs stream or http request stream
(new EmitterStream(
["hello ", "world !"]
.map(function(str) {
return Buffer.from(str, 'utf8');
})
)).pipe(new StreamReducer(
function (acc, v) {
acc.push(v);
return acc;
},
[],
function(err, chunks) {
console.log(Buffer.concat(chunks).toString('utf8'));
})
);
这对我有用,基于Node v6.7.0 docs:
let output = '';
stream.on('readable', function() {
let read = stream.read();
if (read !== null) {
// New stream data is available
output += read.toString();
} else {
// Stream is now finished when read is null.
// You can callback here e.g.:
callback(null, output);
}
});
stream.on('error', function(err) {
callback(err, null);
})
上面的Sebastian J做得好。
我遇到了“缓冲区问题”,其中包含几行测试代码,并添加了编码信息并解决了该问题,请参见下文。
软件
// process.stdin.setEncoding('utf8');
process.stdin.on('data', (data) => {
console.log(typeof(data), data);
});
输入
hello world
输出
object <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64 0d 0a>
软件
process.stdin.setEncoding('utf8'); // <- Activate!
process.stdin.on('data', (data) => {
console.log(typeof(data), data);
});
输入
hello world
输出
string hello world
列出的所有答案似乎都是在流动模式下打开可读流,这不是NodeJS的默认设置,并且由于缺少NodeJS在“暂停的可读流”模式下提供的反压支持,因此存在局限性。这是使用Just Buffer,本机流和本机流转换的实现,并支持对象模式
import {Transform} from 'stream';
let buffer =null;
function objectifyStream() {
return new Transform({
objectMode: true,
transform: function(chunk, encoding, next) {
if (!buffer) {
buffer = Buffer.from([...chunk]);
} else {
buffer = Buffer.from([...buffer, ...chunk]);
}
next(null, buffer);
}
});
}
process.stdin.pipe(objectifyStream()).process.stdout
使用您可能已经在项目依赖项中拥有的非常流行的stream-buffers
软件包,这非常简单:
// imports
const { WritableStreamBuffer } = require('stream-buffers');
const { promisify } = require('util');
const { createReadStream } = require('fs');
const pipeline = promisify(require('stream').pipeline);
// sample stream
let stream = createReadStream('/etc/hosts');
// pipeline the stream into a buffer, and print the contents when done
let buf = new WritableStreamBuffer();
pipeline(stream, buf).then(() => console.log(buf.getContents().toString()));
在我的情况下,内容类型响应标头为Content-Type:text / plain。因此,我已经从Buffer中读取了数据,例如:
let data = [];
stream.on('data', (chunk) => {
console.log(Buffer.from(chunk).toString())
data.push(Buffer.from(chunk).toString())
});
你怎么看待这件事 ?
// lets a ReadableStream under stream variable
const chunks = [];
for await (let chunk of stream) {
chunks.push(chunk)
}
const buffer = Buffer.concat(chunks);
const str = buffer.toString("utf-8")