使用AWS SDK for Node.js将二进制文件上传到S3

72
更新: 供将来参考,亚马逊现在已经根据提问时的情况更新了文档。按照下面@Loren Segal的评论所述:

我们已经在最新的预览版中更正了文档,以正确记录此参数。很抱歉造成混淆!



我正在尝试使用AWS SDK for Node.Js的开发人员预览版,并希望使用putObject将压缩的tarball上传到S3。

根据文档Body参数应为...

Body - (Base64编码数据)

...因此,我正在尝试以下代码...

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

// For dev purposes only
AWS.config.update({ accessKeyId: 'key', secretAccessKey: 'secret' });

// Read in the file, convert it to base64, store to S3
fs.readFile('myarchive.tgz', function (err, data) {
  if (err) { throw err; }

  var base64data = new Buffer(data, 'binary').toString('base64');

  var s3 = new AWS.S3();
  s3.client.putObject({
    Bucket: 'mybucketname',
    Key: 'myarchive.tgz',
    Body: base64data
  }).done(function (resp) {
    console.log('Successfully uploaded package.');
  });

});

虽然我可以在S3中看到该文件,但如果我下载它并尝试解压缩它,就会出现文件损坏的错误。因此,我的“base64编码数据”的方法似乎有误。

请问有人能帮我使用putObject上传二进制文件吗?

4个回答

65

您不需要将缓冲区转换为base64字符串。只需将body设置为数据即可。


1
好像就是这样了!不确定文档中提到“base64”的原因。 - isNaN1247
12
抱歉混淆了!我们已经在最新的预览版中纠正了文档,以正确记录该参数。 - Loren Segal
3
可以传递流而不是数据吗?比如,我想发送一个大小为50MB的文件。我能否传递一个可读流并将S3客户端管道内部传递给S3? - CHAPa
1
是的,这是可能的。详见此处:https://dev59.com/c2Uo5IYBdhLWcg3wtRbh - AndyD

28

这里有一种使用流(streams)发送文件的方法,这对于大型文件可能是必要的,并且通常会减少内存开销:

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

// For dev purposes only
AWS.config.update({ accessKeyId: 'key', secretAccessKey: 'secret' });

// Read in the file, convert it to base64, store to S3
var fileStream = fs.createReadStream('myarchive.tgz');
fileStream.on('error', function (err) {
  if (err) { throw err; }
});  
fileStream.on('open', function () {
  var s3 = new AWS.S3();
  s3.putObject({
    Bucket: 'mybucketname',
    Key: 'myarchive.tgz',
    Body: fileStream
  }, function (err) {
    if (err) { throw err; }
  });
});

2
当你说“上传的文件不等于原始文件”时,我不确定你的意思。我曾经使用类似这样的代码将二进制MP4文件上传到AWS。听起来你可能是在尝试在浏览器中运行这段代码而不是在NodeJS中?你能具体说明一下吗? - CaptEmulation
1
我也无法使用这种方法上传MP4文件。虽然文件出现在S3存储桶中,但它是损坏的且无法播放。 - Gregir
1
嗯,我不确定为什么你们会遇到问题。我使用这样的代码上传MP4文件,我可以下载并再次播放它们。我不知道这是否有任何区别,但我没有“流式传输”文件。我完全下载它们并播放它们 - 但我不认为这会有任何区别。我使用Ubuntu主机将文件发送到S3。您可以在此处查看我创建此示例的代码:https://github.com/CaptEmulation/soapbubble-cloud/blob/c678121e3fee472a6b3a0ca1d53c14307c68526b/soapbubble.js#L219-L251一个关键的区别是,此代码首先检查文件是否存在。 - CaptEmulation
我认为既然文件已经存在于磁盘上(由用户上传到服务器的磁盘),这意味着流将从磁盘到网络进行,没有中间缓冲。 - securecurve
1
看起来你是正确的,这似乎是Amazon S3库中的实现问题。S3需要定期暂停流,以防止FileStream继续从磁盘读取数据并放入内存中。您可能能够创建一个适配器流实现,它跟踪已读但未发送的总字节数并暂停自身。我没有看到任何简单的参数可以添加以解决这个胶水实现问题。 - CaptEmulation
显示剩余3条评论

13
我可以用这种方式上传我的二进制文件。
var fileStream = fs.createReadStream("F:/directory/fileName.ext");
var putParams = {
    Bucket: s3bucket,
    Key: s3key,
    Body: fileStream
};
s3.putObject(putParams, function(putErr, putData){
    if(putErr){
        console.error(putErr);
    } else {
        console.log(putData);
    }
});

0
我很确定你现在需要转换Buffer。否则你会得到以下错误信息:

类型 'Buffer' 不能赋值给类型 'StreamingBlobTypes | undefined'。

StreamingBlobTypes 在这里定义: https://github.com/awslabs/smithy-typescript/blob/21ee16a06dddf813374ba88728c68d53c3674ae7/packages/types/src/streaming-payload/streaming-blob-common-types.ts#L22 这里有一个示例,计算了转换后的长度并使用了Buffer。请注意,你需要添加ContentLength头部:
import { Readable } from "stream";

  saveCreativeImage(name: string, image: Buffer): Promise<string> {
    const options: PutObjectRequest = {
      ACL: 'bucket-owner-full-control',
      Bucket: EnvConfig.S3_CREATIVES_BUCKET_NAME,
      Key: name,
      Body:  Readable.from(image),
      ContentType: 'image/png',
      ContentLength: image.length
    };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接