【问题标题】:Download file from Amazon S3 using REST API使用 REST API 从 Amazon S3 下载文件
【发布时间】:2017-06-18 11:50:33
【问题描述】:

我有自己的REST API 可以调用以下载文件。 (最后,文件可以存储在不同类型的服务器中......亚马逊s3,本地等......)

要从 s3 获取文件,我应该使用这种方法:

var url = s3.getSignedUrl('getObject', params);

这会给我一个可下载的链接来打电话。

现在,我的问题是,当文件来自该链接时,如何使用自己的 rest API 下载文件?有没有办法重定向呼叫?

我将Hapi 用于我的REST 服务器。

{
    method: "GET", path: "/downloadFile",
    config: {auth: false},
    handler: function (request, reply) {
        // TODO
        reply({})
    }
},

【问题讨论】:

  • 为什么需要链接然后调用该链接来下载文件,为什么不直接下载文件并返回流?
  • @peteb 这不会让一个请求下载 2 次文件吗?客户端将不得不等到文件从服务器下载,然后流给他?
  • 如果您将流返回给请求者,则不会。这样想,您可以从 S3 获取包含该文件内容的流,然后您可以使用 pipe() 将 S3 流直接通过管道返回给请求者。它永远不会下载到中介,中介只是介于两者之间。特别是如果您使用 AWS-SDK unbufferedStream。
  • 如何为此设置 Hapi 请求?你有一个管道请求的例子吗?谢谢
  • 我在 s3 javascript 中没有找到任何对无缓冲流的方法调用

标签: javascript rest amazon-web-services amazon-s3 hapijs


【解决方案1】:

不要使用重定向来下载所需的文件,而是从 S3 返回一个 unbufferedStream。 unbufferedStream 可以从AWS-SDK 内的HttpResponse 返回。这意味着不需要从 S3 下载文件,然后将其读入,然后让请求者下载文件。

仅供参考,我在 Express 中使用了这种 getObject() 方法,并且从未使用过 Hapi,但是我认为我与路线定义非常接近,但希望它能够抓住我想要实现的目标的精髓。

Hapi.js 路由

const getObject = require('./getObject');

{
    method: "GET", path: "/downloadFile",
    config: {auth: false},
    handler: function (request, reply) {
        let key = ''; // get key from request
        let bucket = ''; // get bucket from request

        return getObject(bucket, key)
          .then((response) => {
            reply.statusCode(response.statusCode);

            response.headers.forEach((header) => {
              reply.header(header, response.headers[header]);
            });

            return reply(response.readStream);
          })
          .catch((err) => {
            // handle err
            reply.statusCode(500);
            return reply('error');
          });
    }
},

getObject.js

const AWS = require('aws-sdk');
const S3 = new AWS.S3(<your-S3-config>);

module.exports = function getObject(bucket, key) {
  return new Promise((resolve, reject) => {
    // Get the file from the bucket
    S3.getObject({
      Bucket: bucket,
      Key: key
    })
      .on('error', (err) => {            
        return reject(err);
      })
      .on('httpHeaders', (statusCode, headers, response) => {
        // If the Key was found inside Bucket, prepare a response object
        if (statusCode === 200) {
          let responseObject = {
            statusCode: statusCode,
            headers: {
              'Content-Disposition': 'attachment; filename=' + key
            }
          };

          if (headers['content-type'])
            responseObject.headers['Content-Type'] = headers['content-type'];
          if (headers['content-length'])
            responseObject.headers['Content-Length'] = headers['content-length'];

          responseObject.readStream = response.httpResponse.createUnbufferedStream();
          return resolve(responseObject);
        }
      })
      .send();
  });
}

【讨论】:

  • 非常感谢,我正在尝试,我会告诉你:)
【解决方案2】:

返回 HTTP 303 重定向,并将 Location 标头设置为 S3 存储桶中 blob 的公共 URL。

如果您的存储桶是私有的,那么您需要代理请求而不是执行重定向,除非您的客户端也可以访问该存储桶。

【讨论】:

  • 如何代理请求?
  • 应该不需要代理请求。 HTTP/1.1 302 FoundLocation: https://${signed_url}
  • @Michael-sqlbot 如果 S3 存储桶是私有的和/或 OP 不想向最终用户/消费者披露 blob URI。
  • 这仍然适用。如果存储桶是私有的,那么此时生成并用于重定向的预签名 URL,具有较短的到期时间,可以允许单个用户立即下载文件,而不是稍后。我的设置为5秒。他们可以知道他们喜欢的所有 blob URI,但 5 秒后,这对他们没有任何好处。 :) 预签名 URL 的过期时间只在请求到达 S3 时检查,所以你不必在它过期之前完成下载,你只需要在它过期之前开始下载。
猜你喜欢
  • 1970-01-01
  • 2012-10-10
  • 2012-08-29
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-29
  • 2014-11-28
相关资源
最近更新 更多