Lesen Sie die Datei aus dem aws s3-Bucket mit dem Knoten fs


78

Ich versuche, eine Datei zu lesen, die sich in einem aws s3-Bucket befindet

fs.readFile(file, function (err, contents) {
  var myLines = contents.Body.toString().split('\n')
})

Ich konnte eine Datei mit dem Knoten aws-sdk herunterladen und hochladen, aber ich weiß nicht, wie ich sie einfach lesen und den Inhalt analysieren soll.

Hier ist ein Beispiel, wie ich die Datei von s3 lese:

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

4
Inhalt.Körper.zuString () anstelle von Inhalt.Körper
Jason

Antworten:


90

Sie haben ein paar Möglichkeiten. Sie können einen Rückruf als zweites Argument einfügen, das mit jeder Fehlermeldung und dem Objekt aufgerufen wird. Dieses Beispiel stammt direkt aus der AWS-Dokumentation:

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

Alternativ können Sie die Ausgabe in einen Stream konvertieren. Es gibt auch ein Beispiel in der AWS-Dokumentation:

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

Was ist, wenn ich auch ein Versprechen für eine insgesamt bessere asynchrone Behandlung verwenden möchte?
Verveguy

16
@verveguy Sie können die folgenden verwenden:new Promise((resolve, reject) => {s3.getObject(params).createReadStream().on('end', () => { return resolve(); }).on('error', (error) => { return reject(error); }).pipe(file)});
Gustavo Straube

1
@verveguy Abhängig davon, welche Version des Knotens Sie ausführen, verwendet die aws-sdk-Version> 2.3.0 native Versprechen. Sie können auch explizit konfigurieren, welche Versprechensbibliothek Sie verwenden möchten. if (typeof Promise === 'undefined') { console.log("Using Bluebird for Promises"); AWS.config.setPromisesDependency(require('bluebird')); }
Alexhb

Wie können wir feststellen, ob pipe () beendet wurde, damit wir nach dem lokalen Schreiben eine weitere Aufgabe für die Datei ausführen können ...?
Usama

46

Dies wird es tun:

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

23

Da Sie scheinbar eine S3-Textdatei Zeile für Zeile verarbeiten möchten. Hier ist eine Knotenversion, die das Standard-Readline-Modul und AWS 'createReadStream () verwendet.

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
Ich denke, die endVeranstaltung heißt closestattdessen. nodejs.org/api/readline.html#readline_event_close
Jonathan Morales Vélez

3
Wenn Sie komprimierte Quelldateien verarbeiten möchten, können Sie diese auch s3.getObject(params).createReadStream().pipe(zlib.createGunzip())als InputStream verwenden ...
Tobi

8

Hier ist das Beispiel, mit dem ich JSON-Daten von S3 abgerufen und analysiert habe.

    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

Ich konnte noch nicht herausfinden warum, aber der createReadStream/ pipe-Ansatz hat bei mir nicht funktioniert. Ich habe versucht, eine große CSV-Datei (über 300 MB) herunterzuladen, und habe doppelte Zeilen erhalten. Es schien ein zufälliges Problem zu sein. Die endgültige Dateigröße variierte bei jedem Versuch, sie herunterzuladen.

Am Ende habe ich einen anderen Weg gewählt, basierend auf AWS JS SDK-Beispielen :

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

Auf diese Weise funktionierte es wie ein Zauber.


4

Wenn Sie Speicherplatz sparen und jede Zeile als JSON-Objekt erhalten möchten, können Sie fast-csvReadstream erstellen und jede Zeile wie folgt als JSON-Objekt lesen:

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

Ich bevorzuge Buffer.from(data.Body).toString('utf8'). Es unterstützt Codierungsparameter. Bei anderen AWS-Diensten (z. B. Kinesis Streams) möchte möglicherweise jemand die 'utf8'Codierung durch ersetzen '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

Ich hatte genau das gleiche Problem beim Herunterladen von sehr großen Dateien aus S3.

Die Beispiellösung aus AWS-Dokumenten funktioniert einfach nicht:

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

Während diese Lösung funktioniert:

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

Der createReadStreamVersuch abfeuert einfach nicht das end, closeoder erroraus irgendeinem Grund des Rückrufs. Siehe hier dazu.

Ich verwende diese Lösung auch zum Aufschreiben von Archiven in gzip, da das erste (AWS-Beispiel) auch in diesem Fall nicht funktioniert:

        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

Mit der neuen Version von sdk funktioniert die akzeptierte Antwort nicht - sie wartet nicht darauf, dass das Objekt heruntergeladen wird. Das folgende Code-Snippet hilft bei der neuen Version:

// 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 = warte auf s3.getObject (params) .promise () Dies ist der richtige Weg. Danke
lustiger

0

Wenn Sie die Rückrufe vermeiden möchten, können Sie die Funktion sdk .promise () wie folgt nutzen:

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

Ich bin sicher, dass die anderen hier genannten Möglichkeiten ihre Vorteile haben, aber das funktioniert großartig für mich. Aus diesem Thread bezogen (siehe die letzte Antwort von AWS): https://forums.aws.amazon.com/thread.jspa?threadID=116788


1
Was ist getObjectResult in der letzten Zeile?
Felipe Deveza

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.