【问题标题】:cordova-plugin-file-transfer: How do you upload a file to S3 using a signed URL?cordova-plugin-file-transfer:如何使用签名 URL 将文件上传到 S3?
【发布时间】:2015-09-09 15:07:11
【问题描述】:

我可以使用文件选择器和常规 XMLHttpRequest(我用来测试 S3 设置)上传到 S3,但不知道如何使用 cordova 文件传输插件成功完成。

我认为这要么与插件没有构建正确的可签名请求有关,要么与给定的本地文件 uri 不喜欢有关。我尝试过使用从标头到 uri 类型的每一个参数,但文档并没有太大帮助,而且插件源是肉酱。

请求需要签名匹配的字符串是这样的:

PUT


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

有什么想法,或者可能是一个工作代码示例?

【问题讨论】:

    标签: javascript android cordova amazon-s3 ionic


    【解决方案1】:

    有点晚了,但我只是花了几天的时间来解决这个问题,所以如果其他人遇到问题,这就是如何使用 AWS 开发工具包的 javascript 版本来上传图像以创建预签名 URL。

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

    <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 错误时,该签名不匹配。

    因此,您已经创建了预签名 URL,并将其传递给 cordova-plugin-file-transfer 以发出上传文件的 HTTP 请求。当该请求到达亚马逊的服务器时,服务器本身将根据请求标头等构建一个字符串,对其进行散列并将该散列与 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.log 放在那里,string 就是您所追求的。一旦您将传递给此方法的string 与从亚马逊返回的错误消息中的StringToSign 的内容相同,天堂就会打开,您的文件将毫不费力地流入您的存储桶。

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

    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
    

    在客户端,我使用这个带有 cordova-plugin-file-transfer 的预签名 URL,就像这样(我使用的是 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);
    });
    

    运行代码产生签名不匹配错误,&lt;StringToSign&gt; 元素中的字符串如下所示:

    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 标头,它将默认为多部分表单数据。

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

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

    并在客户端上将headers 对象添加到传递给cordova-plugin-file-transfer 的上传方法的options

    var options = {
        fileName: filename,
        mimeType: 'image/jpeg',
        chunkedMode: false,
        httpMethod:'PUT',
        encodeURI: false,
        headers: { // <----- headers object added here
            'Content-Type': 'image/jpeg',
        }
    };
    

    嘿,快!上传现在按预期工作。

    【讨论】:

    • 谢谢你好心的陌生人。距离您的解决方案已经超过 5 年了,现在当我因无法上传文件而感到沮丧时,它对我很有用。再次感谢您,继续做好事!
    【解决方案2】:

    我在使用这个插件时遇到了这样的问题

    我发现上传带有签名的文件的唯一有效方法是 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 和插件的有效解决方案

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-10
      • 2020-09-30
      • 1970-01-01
      • 1970-01-01
      • 2022-08-20
      • 2017-06-12
      • 1970-01-01
      相关资源
      最近更新 更多