javascript - 如何从 Node.js 中的 S3 getObject 获得响应?

标签 javascript node.js amazon-s3 aws-sdk aws-sdk-nodejs

在一个 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

看来这工作正常。但是,当我在其中一个 console.log 上设置断点时,我的 IDE (NetBeans) 会引发错误并拒绝显示数据的值。虽然这可能只是 IDE,但我决定尝试其他方式来使用 getObject

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

这不会输出任何东西。放一个断点表明代码永远不会到达 console.log 中的任何一个。我也试过:

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

但是,这也不会输出任何内容,并且放置断点表明永远不会到达 console.log

我做错了什么?

最佳答案

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

自从我在 2016 年写下这个答案以来,亚马逊已经发布了一个新的 JavaScript SDK, @aws-sdk/client-s3 。这个新版本改进了原来的 getObject() ,它总是返回一个 promise ,而不是通过链接到 .promise() 选择加入 getObject() 。除此之外, response.Body 不再是 Buffer ,而是 Readable|ReadableStream|Blob 之一。这会稍微改变对 response.Data 的处理。这应该会更高效,因为我们可以流式传输返回的数据,而不是将所有内容保存在内存中,但代价是实现起来有点冗长。

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

如果您想使用 Buffer ,就像原始的 getObject() 响应一样,可以通过将 responseDataChunks 包装在 Buffer.concat() 而不是使用 Array#join() 来完成,这在与二进制数据交互时会很有用。需要注意的是,由于 Array#join() 返回一个字符串,所以 Buffer 中的每个 responseDataChunks 实例都会隐式调用 Buffer.toString() 并且将使用默认编码 utf8

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() 而不是直接使用流事件可能更方便使用,但性能更差。它通过在继续之前将所有响应数据 block 读入内存来工作。由于这消除了流式传输的所有好处,因此根据 Node.js 文档不鼓励这种方法。

As this method reads the entire stream into memory, it negates the benefits of streams. It's intended for interoperability and convenience, not as the primary way to consume streams. Documentation Link

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

aws-sdk(原始答案)

从 S3 API 执行 getObject() 时,根据 docs,文件的内容位于 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 对象创建新缓冲区,但如果需要,您可以使用上面的示例来实现。

关于javascript - 如何从 Node.js 中的 S3 getObject 获得响应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36942442/

相关文章:

javascript - .prop() 和 twitter Bootstrap 模式对话框

node.js - 如何将 postman 中的 include_type_name 设置为 true

javascript - 使用 Nodejs 将行添加到 Google Sheet

javascript - 使用phonegap将图像上传到S3,如何?

javascript - 检测浏览器对跨域 XMLHttpRequests 的支持?

javascript - 在 VS 2013 的 MVC 中使用 Foundation Zurb 时样式不起作用

c# - 'Amazon.S3.AmazonS3Client' 的类型初始值设定项抛出异常

python - 如何使用 python 从 S3 存储桶读取 .txt 文件并查看内容?

javascript - JavaScript 图表库的速度比较

node.js - Concourse CI - 在源代码中构建工件,将所有工件传递给下一个任务