使用AFNetworking预先签名的S3 URL从iOS应用程序上传

Vla*_*lad 1 amazon-s3 amazon-web-services afnetworking afnetworking-2

我正在尝试将图像从我的iPhone应用程序上传到S3,然后将S3网址存储回我的rails应用程序.我不应该在iOS应用程序中嵌入凭据,所以我采取的方法是:

我尽力遵循我在网上找到的所有指示,但它不起作用,步骤3的结果返回错误401禁止.由于我是新手,我甚至不知道我做错了什么.

在第2步中,我的代码如下所示:

def getS3Url
  s3 = AWS::S3.new(
    :access_key_id => "MY S3 KEY",
    :secret_access_key => "MY SECRET ACCESS KEY"
  )
  object = s3.buckets[params["bucket"]].objects[params["path"]]
  @s3url = object.url_for(:write, { :expires => 20.minutes.from_now, :secure => true }).to_s
end
Run Code Online (Sandbox Code Playgroud)

从step2返回的url看起来像这样: https://s3.amazonaws.com/myapp-bucket-name/images/avatar/user1.png?AWSAccessKeyId=[access key id]&Expires=[expiration timestamp]&Signature=[Signature]

一旦我得到该URL,我尝试通过执行以下操作发布到它:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:[responseObject valueForKey:@"s3url"] parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
  [formData appendPartWithFileData:jpegData name:@"file" fileName:self.filename mimeType:@"image/png"];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
  NSLog(@"Success: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
  NSLog(@"Error: %@", error);
}];
Run Code Online (Sandbox Code Playgroud)

在这段代码中,我从返回的对象中提取出url [responseObject valueForKey:@"s3url"],然后将其作为要发布的URL传递.但它不起作用.这是我在XCode中运行时的日志:

错误:错误域= AFNetworkingErrorDomain代码= -1011"请求失败:禁止(403)"UserInfo = 0x156daaf0 {NSErrorFailingURLKey = https://s3.amazonaws.com/myapp-bucket-name/images/avatar/user1.png?AWSAccessKeyId = [access key id]&Expires = [expiration timestamp]&Signature = [Signature],NSLocalizedDescription = Request failed:forbidden(403),NSUnderlyingError = 0x156aef90"Request failed:unacceptable content-type:application/xml",AFNetworkingOperationFailingURLResponseErrorKey = {URL:https://s3.amazonaws.com/myapp-bucket-name/images/avatar/user1.png?AWSAccessKeyId=[access key id]&Expires = [expiration timestamp]&Signature = [Signature]} {status code:403,headers {Connection = close; "Content-Type"="application/xml"; Date ="Mon,30 Jun 2014 07:21:33 GMT"; Server = AmazonS3; "转移编码"=身份; "x-amz-id-2"="FJwEeOjV1/osJKgKeHO +/OjXVBEbvW09XxNX2kn1UYIuHswU + LKh0mJODRJDNLXm"; "x-amz-request-id"= 46E84D0967B6D4CD; }}}

在这一点上,我甚至不知道我做错了什么.也许我甚至没有发布到正确的URL.也许我需要做的不仅仅是POST.我花了整个周末试图解决这个问题而失败了.有人可以帮忙吗?谢谢.

jos*_*ama 6

我遇到了类似的"挑战".我必须使用AFNetworking 2.0将图像上传到带有来自我服务器的预签名URL的S3存储桶.在我的许多尝试和错误尝试之一,我得到了相同的403错误,发生在我身上的是我必须在请求中放置正确的标头:

  • Content-Type 使用mime类型的图像
  • x-amz-acl至于public-read我的水桶配置

Content-Length似乎是可选的,请注意我没有在multipart中上传图像.

所以这就是我最终做的事情:

+(void) uploadImage:(UIImage *)image atUrl:(NSString *)url withMimeType:(NSString *)mimeType withSuccess:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure {
    NSData *imageData = UIImageJPEGRepresentation(image, 0.1);
    NSURL *requestURL = [NSURL URLWithString:url];
    AFHTTPSessionManager *client = [[AFHTTPSessionManager alloc] initWithBaseURL:requestURL];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setHTTPMethod:@"PUT"];
    [request setValue:mimeType forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:imageData];
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[imageData length]] forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"public-read" forHTTPHeaderField:@"x-amz-acl"];
    [request setURL:requestURL];

    NSURLSessionDataTask *task = [client dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
        if (error == nil) {
            if (success) {
                success(responseObject);
            }
        } else {
            if (failure) {
                failure(error);
            }
        }
    }];
    [task resume];
}
Run Code Online (Sandbox Code Playgroud)

url是我从服务器获得的预签名网址.检查我在0.1的JPEG压缩,因为您可能需要不同的压缩.在我看来,图像质量并不重要.