Wie erhalte ich eine Antwort von S3 getObject in Node.js?

90

In einem Node.js-Projekt versuche ich, Daten von S3 zurückzugewinnen.

Wenn ich benutze getSignedURL, funktioniert alles:

aws.getSignedUrl('getObject', params, function(err, url){
    console.log(url); 
}); 

Meine Parameter sind:

var params = {
              Bucket: "test-aws-imagery", 
              Key: "TILES/Level4/A3_B3_C2/A5_B67_C59_Tiles.par"

Wenn ich die URL-Ausgabe in die Konsole nehme und in einen Webbrowser einfüge, wird die benötigte Datei heruntergeladen.

Wenn ich jedoch versuche zu verwenden, getObjectbekomme ich alle möglichen merkwürdigen Verhaltensweisen. Ich glaube, ich benutze es nur falsch. Folgendes habe ich versucht:

aws.getObject(params, function(err, data){
    console.log(data); 
    console.log(err); 
}); 

Ausgänge:

{ 
  AcceptRanges: 'bytes',
  LastModified: 'Wed, 06 Apr 2016 20:04:02 GMT',
  ContentLength: '1602862',
  ETag: '9826l1e5725fbd52l88ge3f5v0c123a4"',
  ContentType: 'application/octet-stream',
  Metadata: {},
  Body: <Buffer 01 00 00 00  ... > }

  null

Es scheint also, dass dies richtig funktioniert. Wenn ich jedoch einen Haltepunkt auf eines der console.logs setze , gibt meine IDE (NetBeans) einen Fehler aus und weigert sich, den Wert von Daten anzuzeigen. Obwohl dies nur die IDE sein könnte, habe ich mich entschlossen, andere Verwendungsmöglichkeiten auszuprobieren getObject.

aws.getObject(params).on('httpData', function(chunk){
    console.log(chunk); 
}).on('httpDone', function(data){
    console.log(data); 
});

Dies gibt nichts aus. Das Einfügen eines Haltepunkts zeigt, dass der Code niemals eines der console.logs erreicht. Ich habe auch versucht:

aws.getObject(params).on('success', function(data){
    console.log(data); 
});

Dies gibt jedoch auch nichts aus und das Platzieren eines Haltepunkts zeigt, dass der console.lognie erreicht wird.

Was mache ich falsch?

Sara Tibbetts
quelle
Ist Ihr awsObjekt tatsächlich eine neue Instanz des aws.S3Objekts? Wird die Antwort auch getObject()an eine http-Antwort zurückgegeben oder an eine Datei weitergeleitet?
Peteb
@peteb aws = new AWS.S3(). Die Antwort sollte nicht an eine Datei weitergeleitet werden. Ich muss es im Javascript verwenden
Sara Tibbetts
Ist es also sicher anzunehmen, dass der Inhalt JSON oder XML ist?
Peteb
@peteb auch nicht, sie sind ein benutzerdefiniertes Dateiformat
Sara Tibbetts
Zeigen Sie die Parameter an, die Sie im getObject()Aufruf verwenden. Wenn Sie versuchen, eine signierte URL an getObject zu übergeben, funktioniert das meiner Meinung nach nicht.
Mark B

Antworten:

176

Wenn Sie eine getObject()über die S3-API ausführen, befindet sich der Inhalt Ihrer Datei gemäß den Dokumenten in der BodyEigenschaft, die Sie anhand Ihrer Beispielausgabe sehen können. Sie sollten Code haben, der ungefähr so ​​aussieht

const aws = require('aws-sdk');
const s3 = new aws.S3(); // Pass in opts to S3 if necessary

var getParams = {
    Bucket: 'abc', // your bucket name,
    Key: 'abc.txt' // path to the object you're looking for
}

s3.getObject(getParams, function(err, data) {
    // Handle any error and exit
    if (err)
        return err;

  // No error happened
  // Convert Body from a Buffer to a String

  let objectData = data.Body.toString('utf-8'); // Use the encoding necessary
});

Möglicherweise müssen Sie keinen neuen Puffer aus dem data.BodyObjekt erstellen. Wenn Sie dies benötigen, können Sie das obige Beispiel verwenden, um dies zu erreichen.

peteb
quelle
Die zurückkommenden Daten scheinen also ein BufferObjekt zu sein, mit dem ich nicht vertraut bin. Theoretisch könnte ich verwenden new Buffer(data.Body).toString('utf-8');, um zum Inhalt zu gelangen?
Sara Tibbetts
4
Wenn der Inhalt bereits ein Puffer ist, muss daraus kein neuer Puffer erstellt werden. Einfach machen data.Body.toString('utf-8');. Ein Puffer ist eine Darstellung von Binärdaten im Knoten. Wenn Sie weitere Informationen benötigen, finden Sie hier die Dokumente
peteb
4
Dies funktioniert für Text, aber gibt es eine generische Lösung für den Umgang mit Textdateien sowie .png, .jpg usw.?
Carter
4
@carter Dies ist eine allgemeine Lösung. Ändern Sie einfach die .toString('utf8')beim Zugriff data.Bodyauf, .toString('binary')wenn Sie eine Binärzeichenfolge für Bilder möchten. Wenn das BufferIn data.Bodynicht wie in dieser Frage in einen String konvertiert werden muss, können Sie einfach zurückkehren data.Bodyund Bufferdirekt mit dem arbeiten .
Peteb
1
"Body von einem Puffer in einen String konvertieren" ... wäre großartig, wenn die AWS-Dokumente dies etwas klarer machen würden. Ich habe es ziemlich satt, mit AWS zu ringen.
Osullic
29

Basierend auf der Antwort von @peteb, aber mit Promisesund Async/Await:

const AWS = require('aws-sdk');

const s3 = new AWS.S3();

async function getObject (bucket, objectKey) {
  try {
    const params = {
      Bucket: bucket,
      Key: objectKey 
    }

    const data = await s3.getObject(params).promise();

    return data.Body.toString('utf-8');
  } catch (e) {
    throw new Error(`Could not retrieve file from S3: ${e.message}`)
  }
}

// To retrieve you need to use `await getObject()` or `getObject().then()`
getObject('my-bucket', 'path/to/the/object.txt').then(...);
Arian Acosta
quelle
5
Das .promise () am Ende von getObject () war der Schlüssel für mich. Ich finde das AWS SDK manchmal etwas unintuitiv.
Andrew Harris
Meine Antwort lautet 'Promise {<pending>}'
Jonask
1
@jonask getObject()ist eine asynchrone Funktion. Haben Sie versucht, sie aufzurufen await getObject(...)?
Arian Acosta
6

Für jemanden, der nach einer NEST JS TYPESCRIPTVersion der oben genannten sucht :

    /**
     * to fetch a signed URL of a file
     * @param key key of the file to be fetched
     * @param bucket name of the bucket containing the file
     */
    public getFileUrl(key: string, bucket?: string): Promise<string> {
        var scopeBucket: string = bucket ? bucket : this.defaultBucket;
        var params: any = {
            Bucket: scopeBucket,
            Key: key,
            Expires: signatureTimeout  // const value: 30
        };
        return this.account.getSignedUrlPromise(getSignedUrlObject, params);
    }

    /**
     * to get the downloadable file buffer of the file
     * @param key key of the file to be fetched
     * @param bucket name of the bucket containing the file
     */
    public async getFileBuffer(key: string, bucket?: string): Promise<Buffer> {
        var scopeBucket: string = bucket ? bucket : this.defaultBucket;
        var params: GetObjectRequest = {
            Bucket: scopeBucket,
            Key: key
        };
        var fileObject: GetObjectOutput = await this.account.getObject(params).promise();
        return Buffer.from(fileObject.Body.toString());
    }

    /**
     * to upload a file stream onto AWS S3
     * @param stream file buffer to be uploaded
     * @param key key of the file to be uploaded
     * @param bucket name of the bucket 
     */
    public async saveFile(file: Buffer, key: string, bucket?: string): Promise<any> {
        var scopeBucket: string = bucket ? bucket : this.defaultBucket;
        var params: any = {
            Body: file,
            Bucket: scopeBucket,
            Key: key,
            ACL: 'private'
        };
        var uploaded: any = await this.account.upload(params).promise();
        if (uploaded && uploaded.Location && uploaded.Bucket === scopeBucket && uploaded.Key === key)
            return uploaded;
        else {
            throw new HttpException("Error occurred while uploading a file stream", HttpStatus.BAD_REQUEST);
        }
    }
Chaos Legion
quelle
4

Alternativ können Sie die minio-js-Clientbibliothek get-object.js verwenden

var Minio = require('minio')

var s3Client = new Minio({
  endPoint: 's3.amazonaws.com',
  accessKey: 'YOUR-ACCESSKEYID',
  secretKey: 'YOUR-SECRETACCESSKEY'
})

var size = 0
// Get a full object.
s3Client.getObject('my-bucketname', 'my-objectname', function(e, dataStream) {
  if (e) {
    return console.log(e)
  }
  dataStream.on('data', function(chunk) {
    size += chunk.length
  })
  dataStream.on('end', function() {
    console.log("End. Total size = " + size)
  })
  dataStream.on('error', function(e) {
    console.log(e)
  })
})

Haftungsausschluss: Ich arbeite für Minio. Sein Open-Source-S3-kompatibler Objektspeicher wurde in Golang mit Client-Bibliotheken geschrieben, die in Java , Python , Js und Golang verfügbar sind .

koolhead17
quelle
Versucht mino, aber wie man Pufferdaten erhält, wenn ich dataStream drucke. Body es gibt 'undefined'. dh console.log ('datastream', dataStream.Body); // undefined
Dibish
3

Auf den ersten Blick sieht es nicht so aus, als ob Sie etwas falsch machen, aber Sie zeigen nicht Ihren gesamten Code. Folgendes funktionierte für mich, als ich S3 und Node zum ersten Mal auscheckte:

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

if (typeof process.env.API_KEY == 'undefined') {
    var config = require('./config.json');
    for (var key in config) {
        if (config.hasOwnProperty(key)) process.env[key] = config[key];
    }
}

var s3 = new AWS.S3({accessKeyId: process.env.AWS_ID, secretAccessKey:process.env.AWS_KEY});
var objectPath = process.env.AWS_S3_FOLDER +'/test.xml';
s3.putObject({
    Bucket: process.env.AWS_S3_BUCKET, 
    Key: objectPath,
    Body: "<rss><data>hello Fred</data></rss>",
    ACL:'public-read'
}, function(err, data){
    if (err) console.log(err, err.stack); // an error occurred
    else {
        console.log(data);           // successful response
        s3.getObject({
            Bucket: process.env.AWS_S3_BUCKET, 
            Key: objectPath
        }, function(err, data){
            console.log(data.Body.toString());
        });
    }
});
Ritter
quelle