Question:
I am pulling my hair out on this one… I am using the ASIHTTPRequest (http://allseeing-i.com/ASIHTTPRequest/) wrapper library for accessing Amazon S3 storage. I am able to connect just fine and grab a listing of buckets without any problems. My frustration is with trying to UPLOAD (PUT and/or POST) a new object (a photo) to an existing bucket. I am following Amazon’s documentation to the letter, (at least I think I am.) but nothing seems to work.
Please, someone help me before I jump out of a window. I don’t want to die. 🙁
Thanks in advance for ANY help I can get.
L.
Answer:
Here is a basic example using PUT. Obviously you should be using a queue rather than a synchronous request in the real world.
If you change the amz headers, don’t forget to update ‘canonicalizedAmzHeaders’ as per Amazon’s instructions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#import "ASIHTTPRequest.h" #import ... - (void)testS3 { NSString *filePath = @"/path/to/file"; NSString *contentType = @"text/plain"; NSString *bucket = @"mybucket"; NSString *path = @"test"; NSString *secretAccessKey = @"my-secret-access-key"; NSString *accessKey = @"my-access-key"; NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss zzzz"]; NSString *date = [dateFormatter stringFromDate:[NSDate date]]; ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com/%@",bucket,path]]] autorelease]; [request setPostBodyFilePath:filePath]; [request setShouldStreamPostDataFromDisk:YES]; [request setRequestMethod:@"PUT"]; [request addRequestHeader:@"x-amz-acl" value:@"private"]; [request addRequestHeader:@"Content-Type" value:contentType]; [request addRequestHeader:@"Date" value:date]; NSString *canonicalizedAmzHeaders = @"x-amz-acl:private"; NSString *canonicalizedResource = [NSString stringWithFormat:@"/%@/%@",bucket,path]; NSString *stringToSign = [NSString stringWithFormat:@"PUT\n\n%@\n%@\n%@\n%@",contentType,date,canonicalizedAmzHeaders,canonicalizedResource]; NSString *signature = [self base64forData:[self HMACSHA1withKey:secretAccessKey forString:stringToSign]]; NSString *auth = [NSString stringWithFormat:@"AWS %@:%@",accessKey,signature]; [request addRequestHeader:@"Authorization" value:auth]; [request start]; NSLog(@"%@",[request responseString]); } // Source: http://stackoverflow.com/questions/476455/is-there-a-library-for-iphone-to-work-with-hmac-sha-1-encoding - (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string { NSData *clearTextData = [string dataUsingEncoding:NSUTF8StringEncoding]; NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0}; CCHmacContext hmacContext; CCHmacInit(&hmacContext, kCCHmacAlgSHA1, keyData.bytes, keyData.length); CCHmacUpdate(&hmacContext, clearTextData.bytes, clearTextData.length); CCHmacFinal(&hmacContext, digest); return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; } //Source http://www.cocoadev.com/index.pl?BaseSixtyFour - (NSString *)base64forData:(NSData *)data { static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if ([data length] == 0) return @""; char *characters = malloc((([data length] + 2) / 3) * 4); if (characters == NULL) return nil; NSUInteger length = 0; NSUInteger i = 0; while (i < [data length]) { char buffer[3] = {0,0,0}; short bufferLength = 0; while (bufferLength < 3 && i < [data length]) buffer[bufferLength++] = ((char *)[data bytes])[i++]; // Encode the bytes in the buffer to four characters, including padding "=" characters if necessary. characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2]; characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; if (bufferLength > 1) characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; else characters[length++] = '='; if (bufferLength > 2) characters[length++] = encodingTable[buffer[2] & 0x3F]; else characters[length++] = '='; } return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease]; } |