cordova-plugin-file-transfer:如何使用签名URL将文件上传到S3?

时间:2015-06-23 17:10:29

标签: javascript android cordova amazon-s3 ionic

我可以使用文件选择器和常规XMLHttpRequest(我用来测试S3设置)上传到S3,但无法弄清楚如何使用cordova文件传输插件成功完成。< / p>

我认为要么是插件没有构建正确的可签名请求,要么不喜欢给定的本地文件uri。我尝试过使用从头文件到uri类型的每个参数,但文档没什么帮助,插件源是bolognese。

请求需要签名匹配的字符串如下:

PUT


1391784394
x-amz-acl:public-read
/the-app/317fdf654f9e3299f238d97d39f10fb1

任何想法,或者可能是一个有效的代码示例?

2 个答案:

答案 0 :(得分:3)

有点晚了,但我花了几天时间努力解决这个问题,以防其他人遇到问题,这就是设法使用AWS SDK的javascript版本上传图片来创建预先签名的网址。< / p>

解决问题的关键在于从Amazon返回的XML StringToSign错误的SignatureDoesNotMatch元素。就我而言,它看起来像这样:

<StringToSign>
    PUT\n\nmultipart/form-data; boundary=+++++org.apache.cordova.formBoundary\n1481366396\n/bucketName/fileName.jpg
</StringToSign>

当您使用aws-sdk生成预先签名的URL以上传到S3时,它会在内部根据您要生成的请求的各种元素构建一个字符串,然后使用您的AWS创建它的SHA1哈希值秘密。此哈希是作为参数附加到URL的签名,以及当您收到SignatureDoesNotMatch错误时不匹配的签名。

因此,您已创建了预先签名的网址,并将其传递给cordova-plugin-file-transfer,以使您的HTTP请求上传文件。当该请求到达Amazon的服务器时,服务器本身将根据请求标头等构建一个字符串,对其进行散列并将该散列与URL上的签名进行比较。如果散列不匹配,则返回可怕的......

The request signature we calculated does not match the signature you provided. Check your key and signing method.

我在上面提到的StringToSign元素的内容是服务器构建的字符串,以及与预签名URL上的签名进行比较的哈希值。因此,为避免出现错误,您需要确保aws-sdk构建的字符串与服务器构建的字符串相同。

经过一番挖掘后,我最终找到了负责在aws-sdk中创建要散列的字符串的代码。它位于(版本2.7.12):

node_modules/aws-sdk/lib/signers/s3.js

在第168行的底部有一个sign方法:

sign: function sign(secret, string) { return AWS.util.crypto.hmac(secret, string, 'base64', 'sha1'); }

如果你在其中加console.logstring就是你所追求的。一旦你将传递给此方法的string与从亚马逊返回的错误消息中的StringToSign的内容相同,天空将会打开,您的文件将毫不费力地流入您的存储桶。< / p>

在运行node.js的服务器上,我最初创建了我的预签名网址:

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

var s3 = new AWS.S3(options = {
    endpoint: 'https://s3-eu-west-1.amazonaws.com',
    accessKeyId: "ACCESS_KEY",
    secretAccessKey: "SECRET_KEY"
});

var params = {
    Bucket: 'bucketName',
    Key: imageName,
    Expires: 60
};

var signedUrl = s3.getSignedUrl('putObject', params);

//return signedUrl

这产生了一个像这样的签名字符串,类似于OP:

PUT


1481366396
/bucketName/fileName.jpg

在客户端,我使用这个预先签名的URL与cordova-plugin-file-transfer一样(我正在使用Ionic 2,因此插件包装在他们的原生包装中):

let success = (result: any) : void => { 
    console.log("upload success");
}

let failed = (err: any) : void => {
    let code = err.code;
    alert("upload error - " + code);
}

let ft = new Transfer();

var options = {
    fileName: filename,
    mimeType: 'image/jpeg',
    chunkedMode: false,
    httpMethod:'PUT',
    encodeURI: false,
};

ft.upload(localDataURI, presignedUrlFromServer, options, false)
.then((result: any) => {
    success(result);
}).catch((error: any) => {
    failed(error);
});

运行生成的代码签名与错误不匹配,<StringToSign>元素中的字符串如下所示:

PUT

multipart/form-data; boundary=+++++org.apache.cordova.formBoundary
1481366396
/bucketName/fileName.jpg

因此,我们可以看到cordova-plugin-file-transfer已在其自己的Content-Type标头中添加,这导致签名字符串出现差异。在与传递给上传方法的选项对象相关的docs中,它说:

headers: A map of header name/header values. Use an array to specify more than one value. On iOS, FireOS, and Android, if a header named Content-Type is present, multipart form data will NOT be used. (Object)

所以基本上,如果没有设置Content-Type标题,它将默认为多部分表单数据。

好的,现在我们知道问题的原因,这是一个非常简单的修复。在服务器端,我向传递给S3 ContentType方法的params对象添加了getSignedUrl

var params = {
    Bucket: 'bucketName',
    Key: imageName,
    Expires: 60,
    ContentType: 'image/jpeg' // <---- content type added here
};

并在客户端上向headers传递给options上传方法的cordova-plugin-file-transfer添加了var options = { fileName: filename, mimeType: 'image/jpeg', chunkedMode: false, httpMethod:'PUT', encodeURI: false, headers: { // <----- headers object added here 'Content-Type': 'image/jpeg', } }; 个对象:

public class Account extends Controller {
  public static class PasswordChange {
    @MinLength(5)
    @Required
    public String password;

    @MinLength(5)
    @Required
    public String repeatPassword;

    public String validate() {
      if (password == null || !password.equals(repeatPassword)) {
        return Messages.get("playauthenticate.change_password.error.passwords_not_same");
      } else {
        return null;
      }
    }
    // ... 
  }

  // ...
  private final Form<Account.PasswordChange> PASSWORD_CHANGE_FORM;
}
哎呀,嘿!上传现在按预期工作。

答案 1 :(得分:-1)

我遇到了这个插件的问题

我发现上传带签名的文件的唯一工作方式是Christophe Coenraets的方法:http://coenraets.org/blog/2013/09/how-to-upload-pictures-from-a-phonegap-app-to-amazon-s3/

使用此方法,您可以使用cordova-plugin-file-transfer

上传文件

首先,我想使用服务器上的aws-sdk与getSignedUrl()进行签名 它返回已签名的链接,您只需上传到它。

但是,使用插件总是以403结尾:签名不匹配

它可能与内容长度参数有关,但我现在还没有找到使用aws-sdk和插件的工作解决方案