I am trying to upload an image from my iPhone app to S3 and then store the S3 url back into my rails app. I am not supposed to embed credentials in the iOS app so the approach i'm taking is to:
- Step 1. iPhone app sends a request to my rails server to return a pre-signed S3 URL for uploading the image.
- Step 2. Rails server uses
aws-sdk
gem to generate and return a pre-signed URL How to store data in S3 and allow user access in a secure way with rails API / iOS client? - Step 3. iPhone app uses AFNetworking to post the NSData of the image to S3.
I did my best to follow all the directions I found online but it's not working and the result of step 3 returns Error 401 forbidden. Since I am a newbie at this I don't even know what I am doing wrong.
In Step 2, my code looks like this:
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
The url returned from step2 looks something like this: https://s3.amazonaws.com/myapp-bucket-name/images/avatar/user1.png?AWSAccessKeyId=[access key id]&Expires=[expiration timestamp]&Signature=[Signature]
And once i get that URL i try to post to it by doing the following:
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);
}];
In this code I extract the url out from the returned object with [responseObject valueForKey:@"s3url"]
, and then pass that as the URL to post. But it doesn't work. Here's the log when i run it in XCode:
Error: Error Domain=AFNetworkingErrorDomain Code=-1011 "Request failed: forbidden (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; "Transfer-Encoding" = Identity; "x-amz-id-2" = "FJwEeOjV1/osJKgKeHO+/OjXVBEbvW09XxNX2kn1UYIuHswU+LKh0mJODRJDNLXm"; "x-amz-request-id" = 46E84D0967B6D4CD; } }}
At this point I don't even know what I am doing wrong. Maybe I'm not even posting to the correct URL. Maybe I need to do more than just POST. I spent the entire weekend trying to figure this out and failed. Could someone please help? Thanks.