Download AWS SDK iOS 2.0 S3: Add correct md5

I am trying to upload a file to S3 using the new AWS SDK for iOS 2.0. The download works fine until I set ContentMD5 in the request.

First, I create the file path and url:

NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"s3tmp"];
NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];

      

Then I create a request:

AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.bucket = S3_BUCKETNAME;
uploadRequest.key = s3Key;
uploadRequest.body = tempFileURL;

      

Then I create md5. Conveniently, there is a github project here: https://github.com/JoeKun/FileMD5Hash which creates md5 from a file. However, I overlap with bashs md5 and return the same md5 string. Also, if I don't set ContentMD5 in the request, the download succeeds and the console shows the same md5 line as eTag. So I think the md5 calculated in the next line is correct.

NSString *md5 = [FileHash md5HashOfFileAtPath:tempFilePath];

      

Finally, I add md5 to the uploadRequest:

uploadRequest.contentMD5 = md5;

      

and start the download:

[[transferManager upload:uploadRequest] continueWithBlock:^id(BFTask *task) {
NSError *error = task.error;
if (error) {
NSDictionary *errorUserInfo = error.userInfo;
NSLog(@"Error %@: %@",[errorUserInfo objectForKey:@"Code"],[errorUserInfo objectForKey:@"Message"]);
dispatch_sync(dispatch_get_main_queue(), ^{
[weakSelf uploadFinishedUnsuccessful];
});
}
else {
NSLog(@"Upload success for file \n%@ to \n%@/%@",[tempFileURL absoluteString],S3_BUCKETNAME,s3Key);
dispatch_sync(dispatch_get_main_queue(), ^{
[weakSelf uploadFinishedSuccessful];
});
}
return nil;
}];

      

This always returns an error: Error InvalidDigest: The Content-MD5 you specified was invalid.

So, I tried to wrap md5 in base64 using the built-in iOS method:

NSString *base64EncodedString = [[md5 dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];

      

I have crossed this with another base64 library. It returns the same base64 string, so I think the base64 string is correct. I tried to set this as contentMD5:

uploadRequest.contentMD5 = base64EncodedString;

      

I get the same error: Error InvalidDigest: The Content-MD5 you specified was invalid.

Any idea what I am doing wrong?

Thanks for any answer!

+3


source to share


2 answers


You need to base64 encode the binary representation of the MD5 hash ... not the hexadecimal representation, which you think is similar to what you can do.



The resulting value will be somewhere around 24 characters long if encoded correctly ... and twice as long if done incorrectly.

+3


source


Okay, Michaels' comments got me headed in the right direction. After reading back and forth for a long time, I finally understood what he was trying to tell me. For anyone trying to understand the concept, I am trying to explain: An md5 string like "28e01cf6608332ae51d63af3364d77f2" is a hexadecimal representation of a 16 byte digest. This means that every 2 characters of those 32 characters represent 1 byte.

28 = First byte, e0 = Second byte, 1c = third byte, etc., until you have 16 bytes.

The content-md5 header expects 16 base64-encoded bytes, not hexadecimal representation. Otherwise a very convenient method

[FileHash md5HashOfFileAtPath:tempFilePath]

      



only returns this hexadecimal representation as an NSString. So I could either dig out the bytes before converting the string, or convert the string to NSData. I chose the latter with a piece of code I found here :

//convert the md5 hexadecimal string representation BACK to the NSData byte representation
    NSMutableData *md5Data= [[NSMutableData alloc] init];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [md5 length]/2; i++) {
        byte_chars[0] = [md5 characterAtIndex:i*2];
        byte_chars[1] = [md5 characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [md5Data appendBytes:&whole_byte length:1];
    }

    //base64-encode the NSData byte representation
    NSString *base64EncodedString = [md5Data base64EncodedStringWithOptions:0];

    uploadRequest.contentMD5 = base64EncodedString;

      

This base64encodedString returns a success response finally! Thanks Michael!

+3


source







All Articles