问题描述
我正在实现一些代码来获取图像,使用 sharp 库将其转换为 png 和 jpg 两种格式,并返回两个流以便稍后上传到 S3 存储桶。
我提供了两种不同的解决方案,一种使用 Promise,另一种使用 stream.pipeline。 但是,由于某种原因,管道版本的运行速度比承诺慢得多。
这是重现行为的代码(使用节点 14 运行)
const sharp = require('sharp')
const fs = require('fs')
const util = require('util')
const stream = require('stream')
const pipeline = util.promisify(stream.pipeline);
console.time('resize')
const resizeJobPipeline = async (readableStream) => {
const sharpStream = sharp({
failOnError: false
}).resize({width: 800,height: 800,fit: 'inside'})
// using Passthrough here because in the final code will have to pass this stream to s3 upload
const memoryPng = new stream.Passthrough()
const memoryJpg = new stream.Passthrough()
// Have to await each pipeline sepparately,// if wrap them in a Promise.all,then the images don't get fully processed/become corrupted
await pipeline(readableStream,sharpStream.clone().png(),memoryPng)
await pipeline(readableStream,sharpStream.clone().jpeg(),memoryJpg)
return [memoryPng,memoryJpg]
}
const resizeJobPromise = async (readableStream) => {
const sharpStream = sharp({
failOnError: false
}).resize({width: 800,fit: 'inside'})
const promises = []
promises.push(sharpStream.clone().png().pipe(new stream.Passthrough()))
promises.push(sharpStream.clone().jpeg().pipe(new stream.Passthrough()))
readableStream.pipe(sharpStream)
return await Promise.all(promises)
}
const readStream = fs.createReadStream('big_img.jpg')
// resizeJobPromise(readStream).then(res => {
// res[0].pipe(fs.createWriteStream('resized.png'))
// res[1].pipe(fs.createWriteStream('resized.jpg'))
// console.timeEnd('resize')
// }).catch(err => {
// console.log(err)
// })
resizeJobPipeline(readStream).then(res => {
res[0].pipe(fs.createWriteStream('resized.png'))
res[1].pipe(fs.createWriteStream('resized.jpg'))
console.timeEnd('resize')
}).catch(err => {
console.log(err)
})
如果我运行 resizeJobPipeline 版本,使用大约 20mb 的图像,我的平均执行时间约为 500 毫秒
然而,如果评论这个版本并运行 resizeJobPromise 版本,使用相同的图像,我得到的平均时间只有 ~7ms!
通过依次等待两条管道,我预计可能会得到两倍的时间,但不会是 100 倍。
我读到管道版本使用起来更安全,因为它会自动处理可读错误并关闭可写流以防止内存泄漏,而在承诺版本中,我必须手动处理这些错误。
我在 promise 版本中做错了什么吗?代码背后可能发生了什么才能使其具有如此高的性能?
解决方法
我在承诺版本中做错了什么吗?
是的,您不是在测量流的执行时间。请注意
MapWidth
只是将流对象推入一个数组,将它们传递给 MapHeight
不会等待流完成,而是立即完成流对象。你也可以省略这个函数中的承诺内容。
您应该做的是将流promises.push(sharpStream.clone().png().pipe(new stream.PassThrough()))
promises.push(sharpStream.clone().jpeg().pipe(new stream.PassThrough()))
写入文件/s3 写入流:
Promise.all