克隆节点文件对象以并行使用多个流 (Multer)
Cloning a Node File Object to use multiple streams in parallel (Multer)
是否可以克隆 Node.JS 文件对象?
我已经为 Multer 编写了一个自定义存储驱动程序,它在其构造函数中采用一组存储驱动程序并调用每个驱动程序的 ._handleFile。目标是将一个文件并行保存到多个目的地。
但是,磁盘驱动程序打开的文件流似乎会扰乱任何后续读取。在我的特殊情况下,我正在尝试保存到本地磁盘 + AWS-S3。
通过调试(setTimeouts等)发现:
- 如果文件先上传到S3,写入我本地磁盘的文件是空的。
- 如果文件先写入我的本地磁盘,S3 上传就会毫无错误地终止
所以我的假设是同一文件上的多个流会导致奇怪的问题。
多磁盘驱动程序执行以下操作:
...
var outStream = fs.createWriteStream(finalPath)
file.stream.pipe(outStream)
multer AWS S3 驱动程序执行此操作:
...
var upload = this.s3.upload(params)
我假设图书馆打开了一个流。
我不想先保存文件然后再手动创建两个流。我更愿意以某种方式复制文件对象并将它们发送给每个单独的 ._handleFile 方法。
MultiStorage.prototype._handleFile = async function _handleFile (req, file, cb) {
// I removed some code for this example
...
const results = await Promise.all(drivers.map({ driver }, i) => {
return new Promise((fulfill, reject) => {
// file -> this I believe I need to duplicate
driver._handleFile(req, file, (error, info) => {
fulfill({ info, error })
})
})
....
回答我自己的问题
我写了一个小助手,它创建新的 PassThrough 流,然后在数据进入时写入它们。
const { PassThrough } = require('stream');
// Split stream into $count amount of new streams and return them
const splitStream = (stream, count) => {
const streams = [...Array(count)].map(() => new PassThrough());
stream.on('data', chunk => {
streams.map(s => s.push(chunk));
})
stream.on('end', chunk => {
streams.on('end', () => {
streams.map(s => s.push(null));
})
})
return streams;
}
现在您只需要传递您的新流而不是原始流。
myFn(streams[0]);
myFn(streams[1]);
免责声明:此方法不负责错误处理,可能会导致内存泄漏。您可能需要考虑使用 'stream' 库中的 Pipeline() 包装器。
是否可以克隆 Node.JS 文件对象?
我已经为 Multer 编写了一个自定义存储驱动程序,它在其构造函数中采用一组存储驱动程序并调用每个驱动程序的 ._handleFile。目标是将一个文件并行保存到多个目的地。
但是,磁盘驱动程序打开的文件流似乎会扰乱任何后续读取。在我的特殊情况下,我正在尝试保存到本地磁盘 + AWS-S3。
通过调试(setTimeouts等)发现:
- 如果文件先上传到S3,写入我本地磁盘的文件是空的。
- 如果文件先写入我的本地磁盘,S3 上传就会毫无错误地终止
所以我的假设是同一文件上的多个流会导致奇怪的问题。
多磁盘驱动程序执行以下操作:
...
var outStream = fs.createWriteStream(finalPath)
file.stream.pipe(outStream)
multer AWS S3 驱动程序执行此操作:
...
var upload = this.s3.upload(params)
我假设图书馆打开了一个流。
我不想先保存文件然后再手动创建两个流。我更愿意以某种方式复制文件对象并将它们发送给每个单独的 ._handleFile 方法。
MultiStorage.prototype._handleFile = async function _handleFile (req, file, cb) {
// I removed some code for this example
...
const results = await Promise.all(drivers.map({ driver }, i) => {
return new Promise((fulfill, reject) => {
// file -> this I believe I need to duplicate
driver._handleFile(req, file, (error, info) => {
fulfill({ info, error })
})
})
....
回答我自己的问题
我写了一个小助手,它创建新的 PassThrough 流,然后在数据进入时写入它们。
const { PassThrough } = require('stream');
// Split stream into $count amount of new streams and return them
const splitStream = (stream, count) => {
const streams = [...Array(count)].map(() => new PassThrough());
stream.on('data', chunk => {
streams.map(s => s.push(chunk));
})
stream.on('end', chunk => {
streams.on('end', () => {
streams.map(s => s.push(null));
})
})
return streams;
}
现在您只需要传递您的新流而不是原始流。
myFn(streams[0]);
myFn(streams[1]);
免责声明:此方法不负责错误处理,可能会导致内存泄漏。您可能需要考虑使用 'stream' 库中的 Pipeline() 包装器。