【问题标题】:What's wrong with my code to upload a file to AWS S3 using a pre-signed URL?我使用预签名 URL 将文件上传到 AWS S3 的代码有什么问题?
【发布时间】:2014-02-06 16:39:10
【问题描述】:

我想使用预签名 URL 将文件从 iOS 应用程序上传到 AWS S3 存储桶。该 URL 是正确的,因为它可以在命令行上使用 curl。

curl -v -k --upload-file FILENAME "https://MYBUCKET.amazonaws.com:443/KEYNAME?Signature=...&Expires=1391691489&AWSAccessKeyId=..."

使用以下 Objective-C 代码 ...

- (void)upload:(NSString *)url fileData:(NSData *)fileData
{
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:url]];
    [request setHTTPMethod:@"PUT"];
    [request setHTTPBody:fileData];
    [request setValue:[NSString stringWithFormat:@"%d", [fileData length]] forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"audio/mpeg" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"public-read" forHTTPHeaderField:@"x-amz-acl"];
    [request setValue:@"iPhone-OS/6.0 fr_FR NE" forHTTPHeaderField:@"User-Agent"];

    _connection = [NSURLConnection connectionWithRequest:request delegate:self];
    [_connection start];
}

...我收到此错误:

Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x9c49560 {NSErrorFailingURLStringKey=https://MYBUCKET.s3.amazonaws.com:443/KEYNAME?Signature=...&Expires=1391703958&AWSAccessKeyId=..., NSErrorFailingURLKey=https://MYBUCKET.amazonaws.com:443/KEYNAME?Signature=...&Expires=1391703958&AWSAccessKeyId=..., NSLocalizedDescription=The request timed out., NSUnderlyingError=0x9c48c80 "The request timed out."}

我用WireShark看有没有流量,流量很大。

我不知道我的代码有什么问题。文件传输似乎没有正确终止。

【问题讨论】:

  • 文件有多大,传输大概需要多长时间?
  • 5.5 MB。使用 curl 上传需要几秒钟。我已经尝试将超时设置为 120 秒。但我仍然收到请求超时。
  • 您的 iOS 设备使用什么类型的连接?
  • 我使用模拟器,它是一个快速的 DSL 连接。

标签: ios objective-c amazon-web-services amazon-s3 pre-signed-url


【解决方案1】:

我自己解决了这个问题。 Content-Type 标头是罪魁祸首。完全绝望,我用一个非常小的文本文件测试了我的代码,并得到了 403 作为 S3 的 HTTP 状态代码。没有超时。这样的进步。我还收到了一条非常有用的错误消息:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><StringToSignBytes>...</StringToSignBytes><RequestId>...</RequestId><HostId>...</HostId><SignatureProvided>...</SignatureProvided>
    <StringToSign>PUT

    text/plain
    1391784394
    KEYNAME</StringToSign>
    <AWSAccessKeyId>...</AWSAccessKeyId>
</Error>

如果内容类型字符串(在这种情况下为text/plain),如果它是作为来自客户端的 HTTP 标头提供的,那么它显然应该在字符串签名中。不要问我为什么这会导致 large (5.5MB?) 文件超时。我希望这可以为其他人节省几个小时的生命时间。

最简单的解决方法是删除该行

[request setValue:@"..." forHTTPHeaderField:@"Content-Type"];

如果您在创建预签名 URL 时知道内容类型,那么您当然可以将字符串添加到要签名的字符串中。

【讨论】:

  • 你有 GET 对象的解决方案吗,我遇到了“AccessDenied”的问题。我也在为此苦苦挣扎。
猜你喜欢
  • 2020-10-23
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
相关资源
最近更新 更多