从s3下载文件而不将其写入nodejs中的文件系统

时间:2018-10-17 15:26:31

标签: node.js amazon-s3 stream

我有一个运行Hapi的Nodejs服务器。

服务器的一项工作是将文件发送到服务者API(该API仅在我发送缓冲区时接受流,并且它返回错误),应用户询问

所有文件都存储在s3中。 如果我使用promise()下载它们, 我进入身体缓冲区。 如果我使用的是createReadStream(),则可以通过。

我的问题是,当我尝试将缓冲区转换为流并发送给API拒绝它时,和使用createReadStream()结果时一样, 但是当我使用FS保存文件,然后使用FS读取API时,接受该流及其工作。

所以我需要帮助,如何在不保存和读取文件的情况下创建相同的结果。

编辑: 这是我的代码,我知道这是错误的方法,但是它可以工作,但我需要一种更好的方法来工作

static async downloadFile(Bucket, Key) {
    const result = await s3Client
      .getObject({
        Bucket,
        Key
      })
      .promise();
    fs.writeFileSync(`${Path.basename(Key)}`,result.Body);

    const file = await fs.createReadStream(`${Path.basename(Key)}`);
    return file;
  }

1 个答案:

答案 0 :(得分:0)

如果我对它的理解正确,那么您希望从s3存储桶中获取该对象,并将其作为流传输到HTTP响应。

与其获取缓冲区中的数据,而不是弄清楚将其转换为流的方式可能会很复杂并且有其局限性,如果您真的想利用流的力量,那么不要尝试将其转换为缓冲区并将整个对象加载到内存中,您可以通过在请求上调用createReadStream方法来创建一个将返回的数据直接流式传输到Node.js Stream对象的请求。

调用createReadStream返回由请求管理的原始HTTP流。然后可以将原始数据流通过管道传递到任何Node.js Stream对象中。

此技术对于在有效载荷中返回原始数据的服务调用很有用,例如,在Amazon S3服务对象上调用getObject将数据直接流式传输到文件中,如本示例所示。

//I Imagine you have something similar.
server.get ('/image', (req, res) => {
    let s3 = new AWS.S3({apiVersion: '2006-03-01'});
    let params = {Bucket: 'myBucket', Key: 'myImageFile.jpg'};
    let readStream= s3.getObject(params).createReadStream();
    // When the stream is done being read, end the response
    readStream.on('close', () => {
        res.end()
    })

    readStream.pipe(res);
});

使用createReadStream从请求中流式处理数据时,仅返回原始HTTP数据。 SDK不会对数据进行后处理,可以直接返回原始HTTP数据。

注意: 由于Node.js无法回退大多数流,因此,如果请求最初成功,则将在其余响应中禁用重试逻辑。如果套接字发生故障,则在流传输期间,SDK不会尝试重试或向流发送更多数据。您的应用程序逻辑需要识别并处理此类流失败。

修改: 在对原始问题进行编辑之后,我可以看到s3发送了一个PassThrough流对象,该对象与Nodejs中的FileStream不同。因此,要解决此问题,请使用内存(如果您的文件不是很大,或者您有足够的内存)。

使用软件包memfs,它将替换您应用中的本机fs https://www.npmjs.com/package/memfs

通过npm install memfs安装软件包,并要求如下:

    const {fs} = require('memfs');

您的代码将如下所示

 static async downloadFile(Bucket, Key) {
        const result = await s3
        .getObject({
          Bucket,
          Key
        })
        .promise();
      fs.writeFileSync(`/${Key}`,result.Body);

      const file = await fs.createReadStream(`/${Key}`);
      return file;
    }

请注意,我对您的功能所做的唯一更改是将路径${Path.basename(Key)}更改为/${Key},因为现在您无需知道原始文件系统的路径将文件存储在内存中。我已经测试过并且该解决方案有效