【问题标题】:Read file from aws s3 bucket using node fs使用节点 fs 从 aws s3 存储桶读取文件
【发布时间】:2015-02-02 15:13:37
【问题描述】:

我正在尝试使用

读取 aws s3 存储桶中的文件
fs.readFile(file, function (err, contents) {
  var myLines = contents.Body.toString().split('\n')
})

我已经能够使用节点 aws-sdk 下载和上传文件,但我不知道如何简单地读取它并解析内容。

这是我如何从 s3 读取文件的示例:

var s3 = new AWS.S3();
var params = {Bucket: 'myBucket', Key: 'myKey.csv'}
var s3file = s3.getObject(params)

【问题讨论】:

  • contents.Body.toString() 而不是 contents.Body

标签: node.js amazon-web-services amazon-s3 fs


【解决方案1】:

您有几个选择。您可以包含一个回调作为第二个参数,该参数将与任何错误消息和对象一起调用。这个example 直接来自 AWS 文档:

s3.getObject(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

或者,您可以将输出转换为流。 AWS 文档中还有一个example

var s3 = new AWS.S3({apiVersion: '2006-03-01'});
var params = {Bucket: 'myBucket', Key: 'myImageFile.jpg'};
var file = require('fs').createWriteStream('/path/to/file.jpg');
s3.getObject(params).createReadStream().pipe(file);

【讨论】:

  • 如果我还希望使用 Promise 来更好地进行整体异步处理怎么办?
  • @verveguy 您可以使用以下内容:new Promise((resolve, reject) => {s3.getObject(params).createReadStream().on('end', () => { return resolve(); }).on('error', (error) => { return reject(error); }).pipe(file)});
  • @verveguy 根据您运行的节点版本,aws-sdk 版本 > 2.3.0 将使用本机承诺。您还可以显式配置要使用的承诺库。 if (typeof Promise === 'undefined') { console.log("Using Bluebird for Promises"); AWS.config.setPromisesDependency(require('bluebird')); }
  • 我们如何知道 pipe() 是否已经结束,以便在本地写入文件后对文件执行另一项任务...?
【解决方案2】:

这样就可以了:

new AWS.S3().getObject({ Bucket: this.awsBucketName, Key: keyName }, function(err, data)
{
    if (!err)
        console.log(data.Body.toString());
});

【讨论】:

    【解决方案3】:

    因为您似乎想要逐行处理 S3 文本文件。这是一个使用标准 readline 模块和 AWS 的 createReadStream() 的 Node 版本

    const readline = require('readline');
    
    const rl = readline.createInterface({
        input: s3.getObject(params).createReadStream()
    });
    
    rl.on('line', function(line) {
        console.log(line);
    })
    .on('close', function() {
    });
    

    【讨论】:

    【解决方案4】:

    这是我用来从 s3 检索和解析 json 数据的示例。

        var params = {Bucket: BUCKET_NAME, Key: KEY_NAME};
        new AWS.S3().getObject(params, function(err, json_data)
        {
          if (!err) {
            var json = JSON.parse(new Buffer(json_data.Body).toString("utf8"));
    
           // PROCESS JSON DATA
               ......
         }
       });
    

    【讨论】:

    • 我认为你还需要写下如何处理 json 数据
    • 在第 5 行调用 JSON.parse 后,您将拥有一个常规的 js 对象。如果您的 json 在第 8 行是 "{"name": "John", "id": 1}",您可以调用 json.name
    【解决方案5】:

    我还不知道为什么,但createReadStream/pipe 方法对我不起作用。我试图下载一个大的 CSV 文件(300MB+),我得到了重复的行。这似乎是一个随机的问题。每次尝试下载时,最终文件大小都会有所不同。

    我最终使用了另一种方式,基于AWS JS SDK examples

    var s3 = new AWS.S3();
    var params = {Bucket: 'myBucket', Key: 'myImageFile.jpg'};
    var file = require('fs').createWriteStream('/path/to/file.jpg');
    
    s3.getObject(params).
        on('httpData', function(chunk) { file.write(chunk); }).
        on('httpDone', function() { file.end(); }).
        send();
    

    这样,它就像一个魅力。

    【讨论】:

      【解决方案6】:

      我更喜欢 Buffer.from(data.Body).toString('utf8')。它支持编码参数。对于其他 AWS 服务(例如 Kinesis Streams),有人可能希望将 'utf8' 编码替换为 'base64'

      new AWS.S3().getObject(
        { Bucket: this.awsBucketName, Key: keyName }, 
        function(err, data) {
          if (!err) {
            const body = Buffer.from(data.Body).toString('utf8');
            console.log(body);
          }
        }
      );
      

      【讨论】:

        【解决方案7】:

        如果您希望避免回调,您可以像这样利用 sdk .promise() 函数:

        const s3 = new AWS.S3();
        const params = {Bucket: 'myBucket', Key: 'myKey.csv'}
        const response = await s3.getObject(params).promise() // await the promise
        const fileContent = response.Body.toString('utf-8'); // can also do 'base64' here if desired
        

        我确信这里提到的其他方法各有优势,但这对我来说非常有用。来自此线程(请参阅 AWS 的最后回复):https://forums.aws.amazon.com/thread.jspa?threadID=116788

        【讨论】:

        • 最后一行的getObjectResult是什么?
        • 黄金!但实际上,第 4 行应该是const fileContent = response.Body.toString('utf-8');
        【解决方案8】:

        从 S3 下载非常大的文件时,我遇到了完全相同的问题。

        AWS 文档中的示例解决方案不起作用:

        var file = fs.createWriteStream(options.filePath);
                file.on('close', function(){
                    if(self.logger) self.logger.info("S3Dataset file download saved to %s", options.filePath );
                    return callback(null,done);
                });
                s3.getObject({ Key:  documentKey }).createReadStream().on('error', function(err) {
                    if(self.logger) self.logger.error("S3Dataset download error key:%s error:%@", options.fileName, error);
                    return callback(error);
                }).pipe(file);
        

        虽然此解决方案可行:

            var file = fs.createWriteStream(options.filePath);
            s3.getObject({ Bucket: this._options.s3.Bucket, Key: documentKey })
            .on('error', function(err) {
                if(self.logger) self.logger.error("S3Dataset download error key:%s error:%@", options.fileName, error);
                return callback(error);
            })
            .on('httpData', function(chunk) { file.write(chunk); })
            .on('httpDone', function() { 
                file.end(); 
                if(self.logger) self.logger.info("S3Dataset file download saved to %s", options.filePath );
                return callback(null,done);
            })
            .send();
        

        createReadStream 尝试由于某种原因不会触发 endcloseerror 回调。请参阅here 了解此内容。

        我也在使用该解决方案将存档写入 gzip,因为第一个(AWS 示例)在这种情况下也不起作用:

                var gunzip = zlib.createGunzip();
                var file = fs.createWriteStream( options.filePath );
        
                s3.getObject({ Bucket: this._options.s3.Bucket, Key: documentKey })
                .on('error', function (error) {
                    if(self.logger) self.logger.error("%@",error);
                    return callback(error);
                })
                .on('httpData', function (chunk) {
                    file.write(chunk);
                })
                .on('httpDone', function () {
        
                    file.end();
        
                    if(self.logger) self.logger.info("downloadArchive downloaded %s", options.filePath);
        
                    fs.createReadStream( options.filePath )
                    .on('error', (error) => {
                        return callback(error);
                    })
                    .on('end', () => {
                        if(self.logger) self.logger.info("downloadArchive unarchived %s", options.fileDest);
                        return callback(null, options.fileDest);
                    })
                    .pipe(gunzip)
                    .pipe(fs.createWriteStream(options.fileDest))
                })
                .send();
        

        【讨论】:

          【解决方案9】:

          使用新版本的 sdk,接受的答案不起作用 - 它不会等待下载对象。以下代码 sn -p 将有助于新版本:

          // dependencies
          
          const AWS = require('aws-sdk');
          
          // get reference to S3 client
          
          const s3 = new AWS.S3();
          
          exports.handler = async (event, context, callback) => {
          
          var bucket = "TestBucket"
          
          var key = "TestKey"
          
             try {
          
                const params = {
                      Bucket: Bucket,
                      Key: Key
                  };
          
                 var theObject = await s3.getObject(params).promise();
          
              } catch (error) {
                  console.log(error);
                  return;
              }  
          }
          

          【讨论】:

          • var theObject = await s3.getObject(params).promise() 这是正确的方法。谢谢
          【解决方案10】:

          如果你想节省内存,想将每一行作为json对象获取,那么可以使用fast-csv创建readstream,可以将每一行作为json对象读取,如下:

          const csv = require('fast-csv');
          const AWS = require('aws-sdk');
          
          const credentials = new AWS.Credentials("ACCESSKEY", "SECRETEKEY", "SESSIONTOKEN");
          AWS.config.update({
              credentials: credentials, // credentials required for local execution
              region: 'your_region'
          });
          const dynamoS3Bucket = new AWS.S3();
          const stream = dynamoS3Bucket.getObject({ Bucket: 'your_bucket', Key: 'example.csv' }).createReadStream();
          
          var parser = csv.fromStream(stream, { headers: true }).on("data", function (data) {
              parser.pause();  //can pause reading using this at a particular row
              parser.resume(); // to continue reading
              console.log(data);
          }).on("end", function () {
              console.log('process finished');
          });
          

          【讨论】:

            【解决方案11】:
            var fileStream = fs.createWriteStream('/path/to/file.jpg');
            var s3Stream = s3.getObject({Bucket: 'myBucket', Key: 'myImageFile.jpg'}).createReadStream();
            
            // Listen for errors returned by the service
            s3Stream.on('error', function(err) {
                // NoSuchKey: The specified key does not exist
                console.error(err);
            });
            
            s3Stream.pipe(fileStream).on('error', function(err) {
                // capture any errors that occur when writing data to the file
                console.error('File Stream:', err);
            }).on('close', function() {
                console.log('Done.');
            });
            

            参考:https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/requests-using-stream-objects.html

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-10-11
              • 2017-09-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-14
              • 1970-01-01
              • 2019-09-19
              相关资源
              最近更新 更多