【问题标题】:NodeJS How do I Download a file to disk from an aws s3 bucket?NodeJS 如何从 aws s3 存储桶将文件下载到磁盘?
【发布时间】:2014-04-04 07:35:11
【问题描述】:

我的目标:

显示一个对话框,提示用户保存从 aws 下载的文件。

我的问题:

我目前正在使用 awssum-amazon-s3 创建下载流。但是,我只设法将文件保存到我的服务器或将其流式传输到命令行...正如您从我的代码中看到的那样,我最后一次尝试是尝试手动设置失败的内容处置标头。我不能使用 res.download() 因为已经设置了标头?

我怎样才能实现我的目标?

我的节点代码:

app.post('/dls/:dlKey', function(req, res, next){
        // download the file via aws s3 here
        var dlKey = req.param('dlKey');

        Dl.findOne({key:dlKey}, function(err, dl){
            if (err) return next(err);
            var files = dl.dlFile;

            var options = {
                BucketName    : 'xxxx',
                ObjectName    : files,
            };

            s3.GetObject(options, { stream : true }, function(err, data) {
                // stream this file to stdout
                fmt.sep();
                data.Headers['Content-Disposition'] = 'attachment';
                console.log(data.Headers);
                data.Stream.pipe(fs.createWriteStream('test.pdf'));
                data.Stream.on('end', function() {
                    console.log('File Downloaded!');
                });
            });
        });

        res.end('Successful Download Post!');
    });

我的角度代码:

$scope.dlComplete = function (dl) {
        $scope.procDownload = true;
        $http({
            method: 'POST',
            url: '/dls/' + dl.dlKey
        }).success(function(data/*, status, headers, config*/) {
            console.log(data);
            $location.path('/#!/success');
        }).error(function(/*data, status, headers, config*/) {
            console.log('File download failed!');
        });
    };

此代码的目的是让用户使用生成的密钥下载文件一次。

【问题讨论】:

  • 很遗憾,您无法通过 AJAX 请求将文件下载到用户的磁盘(例如,请参阅 herethere)。您可以做的是让用户发送带有 dlKey 数据的 POST FORM。

标签: node.js amazon-web-services amazon-s3 download disk


【解决方案1】:

这是在最新版本的 aws-sdk 上使用流式传输的整个代码

var express = require('express');
var app = express();
var fs = require('fs');

app.get('/', function(req, res, next){
    res.send('You did not say the magic word');
});


app.get('/s3Proxy', function(req, res, next){
    // download the file via aws s3 here
    var fileKey = req.query['fileKey'];

    console.log('Trying to download file', fileKey);
    var AWS = require('aws-sdk');
    AWS.config.update(
      {
        accessKeyId: "....",
        secretAccessKey: "...",
        region: 'ap-southeast-1'
      }
    );
    var s3 = new AWS.S3();
    var options = {
        Bucket    : '/bucket-url',
        Key    : fileKey,
    };

    res.attachment(fileKey);
    var fileStream = s3.getObject(options).createReadStream();
    fileStream.pipe(res);
});

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('S3 Proxy app listening at http://%s:%s', host, port);
});

【讨论】:

  • 我不知道.createReadStream() 是一个东西。感谢您提供此示例!
  • 如何从客户端下载?流有效,我可以在客户端的控制台上看到。但是我怎样才能在客户端打开“下载对话框”,让用户真正获取文件呢?我猜 Blob 有什么问题,但我不知道是怎么回事
  • var blob = new Blob([data], { type: 'image/jpg' }); 其中数据是角度侧捕获的响应。它下载文件,大小正确,但文件已损坏且无法打开
【解决方案2】:

此代码适用于我最新的库:

var s3 = new AWS.S3();
var s3Params = {
    Bucket: 'your bucket',
    Key: 'path/to/the/file.ext'
};
s3.getObject(s3Params, function(err, res) {
    if (err === null) {
       res.attachment('file.ext'); // or whatever your logic needs
       res.send(data.Body);
    } else {
       res.status(500).send(err);
    }
});

【讨论】:

  • "aws-sdk": "^2.7.20",
  • data.Body 是一个支持 toString() 的 Buffer。 getObject 返回的对象没有 2015 年示例中所示的 .createReadStream() 方法。回调中的数据对象没有 2014 年示例中所示的 data.Stream 属性。
  • 数据未定义。我猜,data.Body 只不过是 result.Body 或 response.Body。如果是这种情况,请更新答案...
【解决方案3】:

只需从 S3 和 WriteStream 创建一个 ReadStream 到您要下载的位置。找到下面的代码。非常适合我:

var AWS = require('aws-sdk');
var path = require('path');
var fs = require('fs');

AWS.config.loadFromPath(path.resolve(__dirname, 'config.json'));
AWS.config.update({
  accessKeyId: AWS.config.credentials.accessKeyId,
  secretAccessKey: AWS.config.credentials.secretAccessKey,
  region: AWS.config.region
});

var s3 = new AWS.S3();
var params = {
  Bucket: '<your-bucket>', 
  Key: '<path-to-your-file>'
};
let readStream = s3.getObject(params).createReadStream();
let writeStream = fs.createWriteStream(path.join(__dirname, 's3data.txt'));
readStream.pipe(writeStream);

【讨论】:

    【解决方案4】:

    您已经知道解决问题最重要的是什么:您可以将来自 S3 的文件流通过管道传输到任何可写流,无论是文件流……还是将发送到客户端的响应流!

    s3.GetObject(options, { stream : true }, function(err, data) {
        res.attachment('test.pdf');
        data.Stream.pipe(res);
    });
    

    注意使用res.attachment 将设置正确的标题。您还可以查看 this answer 关于流和 S3 的信息。

    【讨论】:

    • 感谢您的快速发帖!您建议的代码编译文件,看起来好像应该可以工作,但它仍然没有提示我在 safari 或 chrome 上的下载对话框!我不知道为什么,因为它现在应该有一个附件标题
    • 这种行为(提示用户下载位置)是不是和浏览器的配置有关?
    • 是的,但即使配置正确,我也没有收到提示。它甚至没有下载(服务器或本地)。我也会用我的角度代码更新我的问题,因为也许这就是问题所在。
    • 您可以试试this simple gist 并使用您的浏览器检查文件是否正确下载?如果是,这意味着您的 Angular 代码是问题
    • 我运行它但得到:“错误:ENOENT,打开'test.pdf'”
    【解决方案5】:

    为此,我使用React frontendnode js backend。前端我使用 Axios。我用这个点击按钮下载文件。

    ==== Node js 后端代码 (AWS S3) ======

    //在GET方法里面我调用了这个函数

        public download = (req: Request, res: Response) => {
        const keyName = req.query.keyName as string;
        if (!keyName) {
            throw new Error('key is undefined');
        }
        const downloadParams: AWS.S3.GetObjectRequest = {
            Bucket: this.BUCKET_NAME,
            Key: keyName
        };
    
        this.s3.getObject(downloadParams, (error, data) => {
            if (error) {
                return error;
            }
            res.send(data.Body);
            res.end();
        });
    };
    

    ====== React js 前端代码 ========

    //该函数处理下载按钮onClick

      const downloadHandler = async (keyName: string) => {
      const response = await axiosInstance.get( //here use axios interceptors
        `papers/paper/download?keyName=${keyName}`,{
          responseType:'blob', //very very important dont miss (if not downloaded file unsupported to view)
        }
      );
      const url = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement("a");
      link.href = url;
      link.setAttribute("download", "file.pdf"); //change "file.pdf" according to saved name you want, give extension according to filetype
      document.body.appendChild(link);
      link.click();
      link.remove();
    };
    

    ------或者(如果你使用的是普通的 axios 而不是axios interceptors)-----

    axios({
       url: 'http://localhost:5000/static/example.pdf',
       method: 'GET',
       responseType: 'blob', // very very important
    }).then((response) => {
       const url = window.URL.createObjectURL(new Blob([response.data]));
       const link = document.createElement('a');
       link.href = url;
       link.setAttribute('download', 'file.pdf');
       document.body.appendChild(link);
       link.click();
    });
    

    更多请参考以下文章 1.article 1 2.article 2

    【讨论】:

      【解决方案6】:

      使用 aws SDK v3

      npm install @aws-sdk/client-s3
      

      下载代码

      import { GetObjectCommand } from "@aws-sdk/client-s3";
      /**
       * download a file from AWS and send to your rest client
       */
      app.get('/download', function(req, res, next){
          var fileKey = req.query['fileKey'];
      
          var bucketParams = {
              Bucket: 'my-bucket-name',
              Key: fileKey,
          };
      
          res.attachment(fileKey);
          var fileStream = await s3Client.send(new GetObjectCommand(bucketParams));
          // for TS you can add: if (fileStream.Body instanceof Readable)
          fileStream.Body.pipe(res)
      });
      

      【讨论】:

        猜你喜欢
        • 2019-09-06
        • 2019-02-16
        • 1970-01-01
        • 1970-01-01
        • 2021-05-23
        • 2019-11-25
        • 1970-01-01
        • 2017-10-19
        • 2021-04-11
        相关资源
        最近更新 更多