Зчитування файлу з відра aws s3 за допомогою вузла fs


79

Я намагаюся прочитати файл, який знаходиться у відрі 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)

4
contents.Body.toString () замість content.Body
Джейсон

Відповіді:


90

У вас є пара варіантів. Ви можете включити зворотний виклик як другий аргумент, який буде викликаний з будь-яким повідомленням про помилку та об'єктом. Цей приклад прямо з документації AWS:

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

Крім того, ви можете перетворити висновок у потік. У документації AWS також є приклад :

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

16
@verveguy Ви можете використовувати наступне:new Promise((resolve, reject) => {s3.getObject(params).createReadStream().on('end', () => { return resolve(); }).on('error', (error) => { return reject(error); }).pipe(file)});
Густаво Штрауб

1
@verveguy Залежно від того, яку версію вузла ви використовуєте, aws-sdk версія> 2.3.0, буде використовувати власні обіцянки. Ви також можете чітко налаштувати, яку бібліотеку обіцянок ви хочете використовувати. if (typeof Promise === 'undefined') { console.log("Using Bluebird for Promises"); AWS.config.setPromisesDependency(require('bluebird')); }
alexhb

Як ми можемо дізнатися, чи закінчився pipe (), щоб ми могли виконати інше завдання у файлі після локального запису ...?
usama

46

Це зробить це:

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

24

Оскільки ви, схоже, хочете обробити текстовий файл S3 за рядком. Ось версія Node, яка використовує стандартний модуль зчитування та AWS 'createReadStream ()

const readline = require('readline');

const rl = readline.createInterface({
    input: s3.getObject(params).createReadStream()
});

rl.on('line', function(line) {
    console.log(line);
})
.on('close', function() {
});

1
Я думаю, що endподія називається closeзамість цього. nodejs.org/api/readline.html#readline_event_close
Джонатан Моралес Велес

3
Якщо ви хочете обробляти gzipped вихідні файли, ви також можете використовувати s3.getObject(params).createReadStream().pipe(zlib.createGunzip())як InputStream ...
Тобі,

8

ось приклад, який я використовував для отримання та аналізу даних json з s3.

    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
           ......
     }
   });

8

Я ще не міг зрозуміти, чому, але createReadStream/pipe підхід для мене не спрацював. Я намагався завантажити великий файл CSV (300 МБ +), і я отримав дубльовані рядки. Це здавалося випадковою проблемою. Остаточний розмір файлу змінювався під час кожної спроби завантажити його.

У підсумку я використав інший спосіб, заснований на прикладах AWS JS SDK :

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();

Таким чином, це спрацювало як шарм.


4

Якщо ви хочете заощадити пам'ять і хочете отримати кожен рядок як об'єкт json, тоді ви можете використовувати fast-csvдля створення потоку читання і можете читати кожен рядок як об'єкт 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');
});

4

Я віддаю перевагу 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);
    }
  }
);

3

У мене була точно така ж проблема під час завантаження з 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Спроба просто не звільнити end, closeабо errorна зворотній дзвінок з якої - то причини. Подивіться тут про це.

Я використовую це рішення також для запису архівів у 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();

2

У новій версії sdk прийнята відповідь не працює - вона не чекає завантаження об’єкта. Наступний фрагмент коду допоможе з новою версією:

// 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 () Це правильний шлях. Дякую
веселий

0

Якщо ви хочете уникнути зворотних викликів, ви можете скористатися функцією 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 = getObjectResult.Body.toString('utf-8'); // can also do 'base64' here if desired

Я впевнений, що інші згадані тут способи мають свої переваги, але це для мене чудово працює. Джерело цього потоку (див. Останню відповідь від AWS): https://forums.aws.amazon.com/thread.jspa?threadID=116788


1
Що таке getObjectResult в останньому рядку?
Феліпе Девеза

0
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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.