如何在 Node.js 从 S3 getObject 获得响应?

在 Node.js 项目中,我正在尝试从 S3 获取数据。

当我使用时,一切正常:getSignedURL

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

我的参数是:

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

如果我将 URL 输出带到控制台并将其粘贴到 Web 浏览器中,它将下载我需要的文件。

但是,如果我试图使用,我会遇到各种奇怪的行为。我相信我只是错误地使用它。这是我尝试过的:getObject

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

输出:

{ 
  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

因此,这似乎可以正常工作。但是,当我在其中一个 s 上放置断点时,我的 IDE (NetBeans) 会引发错误并拒绝显示数据的值。虽然这可能只是IDE,但我决定尝试其他方法来使用。console.loggetObject

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

这不会输出任何内容。将断点放入表示代码永远不会到达其中任何一个 s。我还尝试了:console.log

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

但是,这也不会输出任何内容,并且放置断点表示从未达到。console.log

我做错了什么?


答案 1

@aws-sdk/client-s3(2022 年更新)

自从我在2016年写了这个答案以来,亚马逊发布了一个新的JavaScript SDK,@aws-sdk/client-s3。这个新版本通过始终返回承诺而不是通过链接到来选择加入,从而改进了原始版本。除此之外,还有响应。正文不再是缓冲区,而是 .这改变了位的处理方式。这应该更高性能,因为我们可以流式传输返回的数据,而不是将所有内容保存在内存中,权衡是实现起来有点冗长。getObject().promise()getObject()Readable|ReadableStream|Blobresponse.Data

在下面的示例中,数据将被流式传输到数组中,然后作为字符串返回。这是我原始答案的等效示例。或者,可以使用流。Readable.pipe() 到 HTTP 响应、文件或任何其他类型的流。可写入以供进一步使用,这将是获取大型对象时性能更高的方法。response.Bodyresponse.Body

如果要使用 ,如原始响应,可以通过包装在 Buffer.concat() 中而不是使用 Array#join() 来完成此操作,这在与二进制数据交互时非常有用。请注意,由于返回一个字符串,因此 中的每个实例都将隐式调用 Buffer.toString(),并且将使用 的默认编码。BuffergetObject()responseDataChunksArray#join()BufferresponseDataChunksutf8

const { GetObjectCommand, S3Client } = require('@aws-sdk/client-s3')
const client = new S3Client() // Pass in opts to S3 if necessary

function getObject (Bucket, Key) {
  return new Promise(async (resolve, reject) => {
    const getObjectCommand = new GetObjectCommand({ Bucket, Key })

    try {
      const response = await client.send(getObjectCommand)
  
      // Store all of data chunks returned from the response data stream 
      // into an array then use Array#join() to use the returned contents as a String
      let responseDataChunks = []

      // Handle an error while streaming the response body
      response.Body.once('error', err => reject(err))
  
      // Attach a 'data' listener to add the chunks of data to our array
      // Each chunk is a Buffer instance
      response.Body.on('data', chunk => responseDataChunks.push(chunk))
  
      // Once the stream has no more data, join the chunks into a string and return the string
      response.Body.once('end', () => resolve(responseDataChunks.join('')))
    } catch (err) {
      // Handle the error or throw
      return reject(err)
    } 
  })
}

关于使用的评论Readable.toArray()

使用Readable.toArray() 而不是直接处理流事件可能更方便使用,但性能较差。它的工作原理是在继续之前将所有响应数据块读入内存。由于这消除了流式处理的所有好处,因此不建议使用Node.js文档。

当此方法将整个流读取到内存中时,它否定了流的好处。它旨在实现互操作性和便利性,而不是作为使用流的主要方式。文档链接

@aws-sdk/client-s3文档链接

aws-sdk (原始答案)

从 S3 API 执行 a 操作时,根据文档,文件的内容位于属性中,您可以从示例输出中看到该属性。您应该具有如下所示的代码getObject()Body

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

您可能不需要从对象创建新的缓冲区,但如果需要,可以使用上面的示例来实现此目的。data.Body


答案 2

根据@peteb的答案,但使用和:PromisesAsync/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()`
const myObject = await getObject('my-bucket', 'path/to/the/object.txt');