NodeJs sharp图像库 - 使用Stream Api调整大小会引发错误“stream.push() after EOF”

3

有时流(stream)的行为对我来说有些奇怪。 我的工作需要从S3中拉取图像并使用sharp库进行转换;最后将调整大小后的图像上传回S3。 虽然我可以在S3中看到已调整大小的图像,但代码会失败并显示错误。

以下是执行所有工作的代码片段 -

// Read from S3
const readStream = s3Handler.readStream(params)

// resize Stream
let resizeStream = sharp()
resizeStream
    .metadata()
    .then((metadata) => {

        // Resize the image to a width specified by the `percent` value and output as PNG
        sharpOptions = { width: width*(metadata.percent), height: height*(metadata.percent), fit: 'contain' }
        
        resizeStream
            .resize(sharpOptions)
            .toFormat('png')
            .png({ quality: 100, compressionLevel: 5 })
            .toBuffer()

        //return streamResize.resize(sharpOptions).png().toBuffer()
    })

// push to s3
const { writeStream, uploaded } = s3Handler.writeStream({
    Bucket: bucket,
    Key: key,
    Ext: ext,
})


readStream.pipe(resizeStream).pipe(writeStream)

await uploaded

这是我从上面的代码中得到的错误信息 -

{
    "errorType": "Error",
    "errorMessage": "stream.push() after EOF",
    "code": "ERR_STREAM_PUSH_AFTER_EOF",
    "stack": [
        "Error [ERR_STREAM_PUSH_AFTER_EOF]: stream.push() after EOF",
        "    at readableAddChunk (_stream_readable.js:260:30)",
        "    at Sharp.Readable.push (_stream_readable.js:213:10)",
        "    at Object.<anonymous> (/var/task/node_modules/sharp/lib/output.js:863:18)"
    ]
}

非常感谢任何建议。

1个回答

4

您正在尝试使用node-sharp做一些目前有点困难的事情,请参见https://github.com/lovell/sharp/issues/236

简而言之,问题在于.metadata()需要将整个图像读入内存(作为缓冲区)以计算值。除此之外,相对调整大小(原始大小的百分比)会创建数据依赖 - 您需要知道图像的尺寸才能计算目标宽度和高度。

考虑到您正在将Sharp用作流(S3读取→调整大小→S3写入),因此在其上调用.metadata()不起作用,因为它发生得太晚了:您需要在将数据管道传输之前设置调整大小选项(以便调整大小过程可以了解目标大小)。同时,您需要先将所有数据传输到管道中,因为.metadata()需要完整的图像。这两种需求冲突,使得无法拥有一个单独的Sharp流,可以完成所有操作。

您只剩下几个选择:

  1. 放弃基于元数据的动态大小调整,改用静态最大大小(或从其他来源获知的大小-由客户端提交、从配置加载等)
  2. 将整个图像加载到缓冲区中,从中计算元数据,然后调整整个缓冲区的大小
  3. 使用另一种不需要一次性获取全部数据的流技术确定图像大小,并在获得大小时运行管道,就像这样

如果您没有处理非常大的图像,则第2种方法无疑是实现此目的最简单的方法:

const original = (await s3.getObject(getParams).promise()).Body;
const metadata = await sharp(original).metadata();
const resized = await sharp(original).resize({
  // NOTE: This computation was bugged in the original example - there's no metadata.percent, it's our own setting.
  width: metadata.width * percent,
  height: metadata.height * percent,
  fit: 'contain'
}).toFormat('png').png({ quality: 100, compressionLevel: 5 }).toBuffer()
await s3.putObject({
  ...putParams,
  Body: resized
});

谢谢Robert Kawecki。这确实很有帮助。我采用了缓冲区方法而没有使用流。这对我很有效。 - Adeel Ahmad

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