使用Node.js将二进制数据推送到Amazon S3

13

我正试图使用Node.js将一张图片上传到Amazon S3存储桶。最终,我想能够将图片推送到S3,然后能够访问该S3 URL并在浏览器中查看图片。我使用Curl查询执行HTTP POST请求,将图片作为正文发送:

curl -kvX POST --data-binary "@test.jpg" 'http://localhost:3031/upload/image'

然后在Node.js方面,我做了以下操作:

exports.pushImage = function(req, res) {
    var image = new Buffer(req.body);
    var s3bucket = new AWS.S3();
    s3bucket.createBucket(function() {
        var params = {Bucket: 'My/bucket', Key: 'test.jpg', Body: image};
        // Put the object into the bucket.
        s3bucket.putObject(params, function(err) {
            if (err) {
                res.writeHead(403, {'Content-Type':'text/plain'});
                res.write("Error uploading data");
                res.end()
            } else {
                res.writeHead(200, {'Content-Type':'text/plain'});
                res.write("Success");
                res.end()
            }
        });
    });
};
我的文件在Amazon S3上显示为0字节。我该如何使用Node.js将二进制文件上传到S3?我在处理二进制数据和缓冲区方面做错了什么?
更新:
我找到了需要做的事情。应该首先更改curl查询。这是工作正常的命令:
curl -kvX POST -F foobar=@my_image_name.jpg 'http://localhost:3031/upload/image'
然后,我添加了一行将其转换为流的代码。这是可用的代码:
exports.pushImage = function(req, res) {
    var image = new Buffer(req.body);
    var s3bucket = new AWS.S3();
    s3bucket.createBucket(function() {
        var bodyStream = fs.createReadStream(req.files.foobar.path);
        var params = {Bucket: 'My/bucket', Key: 'test.jpg', Body: bodyStream};
        // Put the object into the bucket.
        s3bucket.putObject(params, function(err) {
            if (err) {
                res.writeHead(403, {'Content-Type':'text/plain'});
                res.write("Error uploading data");
                res.end()
            } else {
                res.writeHead(200, {'Content-Type':'text/plain'});
                res.write("Success");
                res.end()
            }
        });
    });
};

因此,为了将文件上传到API端点(使用Node.js和Express),并让API将该文件推送到Amazon S3,首先需要填充“files”字段执行POST请求。文件最终出现在API侧,在那里它可能存储在某个临时目录中。Amazon的S3 putObject方法需要流,因此您需要通过向'fs'模块提供上传文件所在路径来创建读取流。

我不知道这是否是上传数据的正确方式,但它可行。有人知道是否有一种方法在请求正文中POST二进制数据,并让API将其发送到S3吗?我不太清楚多部分上传与标准POST到正文之间的区别。

1个回答

7

我认为您需要按照S3文档中的说明,在标头中传递内容长度: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html

在花费相当多的时间将资产推送到S3后,最终我使用了AwsSum库,并在生产环境中取得了出色的结果:

https://github.com/awssum/awssum-amazon-s3/

(请参见设置AWS凭据的文档)

示例:

var fs = require('fs');
var bucket_name = 'your-bucket name'; // AwsSum also has the API for this if you need to create the buckets

var img_path = 'path_to_file';
var filename = 'your_new_filename';

// using stat to get the size to set contentLength
fs.stat(img_path, function(err, file_info) {

    var bodyStream = fs.createReadStream( img_path );

    var params = {
        BucketName    : bucket_name,
        ObjectName    : filename,
        ContentLength : file_info.size,
        Body          : bodyStream
    };

    s3.putObject(params, function(err, data) {
        if(err) //handle
        var aws_url = 'https://s3.amazonaws.com/' + DEFAULT_BUCKET + '/' + filename;
    });

});

更新

如果你正在使用 Express 或 Connect 等基于 Formidable 的框架,则无法访问文件流,因为 Formidable 会将文件写入磁盘。因此,根据在客户端上的上传方式,图片将出现在 req.bodyreq.files 中。在我的情况下,我使用 Express,在客户端上,我还发布其他数据,所以图像有它自己的参数,并且可以通过 req.files.img_data 访问。无论如何访问它,该参数都是你在上面示例中传递为 img_path 的内容。

如果需要 / 想要串流文件,则更加困难,但肯定是可能的,如果您不操作图像,您可能希望采用 CORS 方法并直接上传到 S3,如此处所述:Stream that user uploads directly to Amazon s3


我在Node.js上接收二进制文件,这意味着如果我的图像在计算机目录中,我就没有像图像路径那样的路径。这是否意味着我不能使用fs库,因为它处理文件系统? - Jack
你是使用express或connect(这两个都使用formidable来解析文件上传)来处理'http://localhost:3031/upload/image'吗? - drivativ
我正在使用Express来处理'localhost:3031/upload/image'。然后,我可以通过查看req.body(请求体)来访问上传的文件。当然,它是一团乱麻。不确定如何将其转换为Amazon S3 pushObject想要的缓冲区。 - Jack
那么你根本不需要将其转换为缓冲区,只需使用 fs.stat(req.body) 获取文件大小,然后使用 fs.createReadStream(req.body) 创建一个流,然后将其作为 Body 参数传递给 S3。 - drivativ
fs.stat(req.body) 报错,需要一个字符串(req.body 是二进制数据,不是字符串)。如果我将二进制的 .jpg 转换为字符串,那么后续就无法使用 Web 浏览器访问它了。 - Jack
嗯...有趣。你可能想检查一下req.files中传递了什么。那是我总是从中获取文件的地方,在这种情况下,它是上传文件的路径。 - drivativ

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