【问题标题】:Download file from s3 without write it to file system in nodejs从 s3 下载文件而不将其写入 nodejs 中的文件系统
【发布时间】:2019-03-22 08:17:36
【问题描述】:

我有一个运行 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;
  }

【问题讨论】:

    标签: node.js amazon-s3 stream


    【解决方案1】:

    如果我理解正确,您希望从 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 发送了一个与 Nodejs 中的 FileStream 不同的 PassThrough 流对象。所以要解决这个问题,请使用内存(如果您的文件不是很大并且或者您有足够的内存)。

    使用包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},因为现在您不需要知道我们将文件存储在内存中的原始文件系统的路径.我已经测试过,这个解决方案有效

    【讨论】:

    • 我的问题是我需要将流发送到 formData 以发送到像这样的其他 API => file[0] = readStream, file[1]=readStrean
    • @YotavMasa 你能把代码贴出来吗?你在 FS 上做得怎么样?我可以用解决方案更新我的答案
    • @YotavMasa 查看我的回答,希望对您有所帮助
    • 但是如果我的缓冲区没有数据,我可以选择让它流式传输而不保存文件?
    • @YotavMasa 可以,但流类型将是 API 不可接受的类型。它必须是 API 所需的 FileStream,因为它需要有关文件统计信息和元数据等文件的信息,如果您可以即时将 Passthrough 流转换为 FileStream“这不是一件容易的事”,那么您可以直接发送流,这就是 MemoryFS 完美的原因,因为它可以将直通流转换为 FileStream。
    猜你喜欢
    • 2022-11-17
    • 2020-04-13
    • 2016-02-02
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 2018-06-30
    相关资源
    最近更新 更多