如何将对象正确上传到 AWS S3?

How to properly upload an object to AWS S3?

经过三天的追逐这个问题。是时候寻求帮助了。 我有一个签名 url 可以将文件上传到 Amazon S3。我知道这是正确的,因为 a

curl -v -T file.png --header "Content-Type:binary/octet-stream" "https://sample.s3.amazonaws.com/33d7e8f0-0fc5-11e5-9d95-2b3410860edd?AWSAccessKeyId=XXXXXXXX&Content-Type=binary%2Foctet-stream&Expires=1433984735&Signature=LUjj8iIAbCfNoskGhqLDhuEWVG4%3D"

正确成功。

但是我的 .ajax 上传代码(如下)使 S3 存储桶中的内容出现轻微乱码。
例如,如果我上传一个 .pdf 文件,它会从 S3 管理控制台正确加载,但如果它是 .png 或 .jpeg 等,它会失败......仔细查看文件长度错误(轻微)。

浏览器调用的核心是:

var formData = new FormData();
formData.append("upload", file);
$.ajax({ url: data.puturl,
         type: 'PUT',
         xhr: function() {
           var myXhr = $.ajaxSettings.xhr();
           if (myXhr.upload) {
             myXhr.upload.addEventListener('progress', progressHandlingFunction, 
                                           false);
           }
           return myXhr;
         },
         success: completeHandler,
         error: errorHandler,
         data: formData,
         cache: $.param(false),
         contentType: "binary/octet-stream",
         processData: $.param(false)
         }, 'json');

这似乎行得通,但数据是乱码。我尝试将内容设置为 file.type 等,但无济于事。我需要在这里做一些编码吗?比如我缺少的base64????

如有任何见解,我们将不胜感激。
或者,如果有一种简单的方法可以不使用 .ajax 来做同样的事情,那就太好了。

根据 'tedder42' 上面提出的问题,以及更多的实验,我意识到发送 FormData 是问题所在。所以我将代码更改为仅使用 FileReader() 并传递原始数据。这非常有效。这是代码:

var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (e) {
  var rawData = reader.result;
  $.ajax({ url: data.puturl,
    type: 'PUT',
    xhr: function() {
      var myXhr = $.ajaxSettings.xhr();
      if (myXhr.upload) {
        myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
      }
      return myXhr;
    },
    success: completeHandler,
    error: errorHandler,
    // Form data
    data: rawData,
    cache: $.param(false),
    contentType: "binary/octet-stream",
    processData: $.param(false)
  }, 'json');
};

这要简单得多,而且一切都很完美。然后后面下载数据的时候用一个带符号的url,我干脆把

ResponseContentDisposition: "attachment; filename="+fileData.name

作为 s3.getSignedUrl('getObject', params) 调用中的参数之一。

AND 在预先签名的 url 中检索文件,我把

ResponseContentDisposition: "attachment; filename="+fileData.name, 'ResponseContentType': fileData.type 

负责确保浏览器期望收到的内容。

因为在使用预签名 url 时上传对象时无法设置内容类型,所以我还在服务器端添加了代码以在上传完成后更改对象内容类型。这是代码的核心:

var params = {
  Bucket: 'sample',
  CopySource: 'sample/' + file.key,
  Key: file.key,
  MetadataDirective: 'REPLACE',
  ContentType: type
};
s3.copyObject(params, function (err, data){
  if (err) {
    console.log(err);
    validationError(res, 'unable to change content-type!');
  }
  res.sendStatus(200);
});

这是一个最终正确的痛苦,我希望这会帮助其他人!