Come ottenere la risposta da S3 getObject in Node.js?


90

In un progetto Node.js sto tentando di recuperare i dati da S3.

Quando lo uso getSignedURL, tutto funziona:

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

I miei parametri sono:

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

Se porto l'URL di output sulla console e lo incollo in un browser web, scarica il file di cui ho bisogno.

Tuttavia, se provo a utilizzare getObjectottengo tutti i tipi di comportamenti strani. Credo di usarlo solo in modo errato. Questo è quello che ho provato:

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

Uscite:

{ 
  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

Quindi sembra che funzioni correttamente. Tuttavia, quando inserisco un punto di interruzione su uno dei messaggi console.log, il mio IDE (NetBeans) genera un errore e si rifiuta di mostrare il valore dei dati. Anche se questo potrebbe essere solo l'IDE, ho deciso di provare altri modi per utilizzarlo getObject.

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

Questo non produce nulla. Mettere un punto di interruzione in mostra che il codice non raggiunge mai nessuno dei console.logs. Ho anche provato:

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

Tuttavia, anche questo non restituisce nulla e l'inserimento di un punto di interruzione mostra che console.lognon viene mai raggiunto.

Che cosa sto facendo di sbagliato?


Il tuo awsoggetto è effettivamente una nuova istanza aws.S3dell'oggetto? Inoltre, la risposta getObject()viene ritrasmessa a una risposta http o viene reindirizzata a un file?
peteb

@peteb aws = new AWS.S3(). La risposta non deve essere reindirizzata a un file. Devo usarlo nel Javascript
Sara Tibbetts

Quindi è lecito presumere che i contenuti siano JSON o XML?
peteb

Neanche @peteb, sono un formato di file personalizzato
Sara Tibbetts,

Mostra i parametri che stai utilizzando nella getObject()chiamata. Se stai tentando di passare un URL firmato a getObject, non penso che funzionerà.
Mark B

Risposte:


177

Quando esegui un'operazione getObject()dall'API S3, secondo i documenti, il contenuto del tuo file si trova nella Bodyproprietà, che puoi vedere dall'output di esempio. Dovresti avere un codice simile al seguente

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

Potrebbe non essere necessario creare un nuovo buffer data.Bodydall'oggetto, ma se necessario è possibile utilizzare l'esempio sopra per ottenere ciò.


Quindi i dati che tornano sembrano essere un Bufferoggetto con cui non ho familiarità. Teoricamente potrei usare new Buffer(data.Body).toString('utf-8');per arrivare al contenuto?
Sara Tibbetts

4
Se il contenuto è già un Buffer, non è necessario creare un nuovo Buffer da quello. Fallo e basta data.Body.toString('utf-8');. Un buffer è una rappresentazione dei dati binari nel nodo, se hai bisogno di maggiori informazioni ecco i documenti
peteb

4
Funziona per il testo, ma esiste una soluzione generica per la gestione di file di testo oltre a .png, .jpg, ecc.?
carter

4
@carter Questa è una soluzione generale. Basta cambiare il .toString('utf8')quando si accede data.Bodya .toString('binary')se si desidera una stringa binaria per le immagini. Se l' Bufferin data.Bodynon ha bisogno di essere convertito in una stringa come in questa domanda, allora si può solo tornare data.Bodye lavorare con la Bufferdiretta.
peteb

1
"Converti corpo da un buffer a una stringa" ... sarebbe fantastico se i documenti AWS lo rendessero un po 'più chiaro. Mi sto abbastanza stufando di lottare con AWS.
osullic

30

Basato sulla risposta di @peteb, ma utilizzando Promisese 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(...);

5
Il .promise () alla fine di getObject () era la chiave per me. A volte trovo l'SDK AWS un po 'poco intuitivo.
Andrew Harris

La mia risposta è "Promise {<pending>}"
jonask

1
@jonask getObject()è una funzione asincrona, hai provato a chiamarla con await getObject(...)?
Arian Acosta

6

Per qualcuno che cerca una NEST JS TYPESCRIPTversione di quanto sopra:

    /**
     * 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);
        }
    }

4

In alternativa potresti usare la libreria client minio-js get-object.js

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

Disclaimer: lavoro per Minio Il suo object storage compatibile con S3 open source scritto in golang con librerie client disponibili in Java , Python , Js , golang .


Ho provato mino, ma come ottenere i dati del buffer, quando stampo dataStream. Il corpo sta dando 'undefined'. ovvero console.log ('datastream', dataStream.Body); // undefined
Dibish

3

A prima vista non sembra che tu stia facendo qualcosa di sbagliato ma non mostri tutto il tuo codice. Quanto segue ha funzionato per me quando stavo controllando per la prima volta S3 e Node:

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());
        });
    }
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.