AWS SDK iOS 2.0 S3上传:添加正确的MD5

4
我将尝试使用新的AWS SDK for iOS 2.0上传文件到S3。只要我不在请求中设置contentMD5,上传就可以正常工作。
首先,我创建了一个文件路径和一个URL:
NSString *tempFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"s3tmp"];
NSURL *tempFileURL = [NSURL fileURLWithPath:tempFilePath];

接下来,我创建请求:

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

我接下来创建md5。方便的是,这里有一个github项目:https://github.com/JoeKun/FileMD5Hash,可以从文件中创建md5。然而,我使用bash的md5进行了交叉检查,它返回了相同的md5字符串。此外,如果我在请求中不设置contentMD5,上传会成功,并且在控制台上显示相同的md5字符串作为eTag。因此,我认为在下一行计算的md5是正确的。

NSString *md5 = [FileHash md5HashOfFileAtPath:tempFilePath];

最后,我将md5添加到uploadRequest中:
uploadRequest.contentMD5 = md5;

并开始上传:

[[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;
}];

这总是返回错误:

Error InvalidDigest: 指定的Content-MD5无效。

所以我尝试将md5包装成base64,使用iOS的内置方法:

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

我用另一个base64库进行了交叉检查。它返回相同的base64字符串,所以我认为这个base64字符串是正确的。我尝试将其设置为contentMD5:

uploadRequest.contentMD5 = base64EncodedString;

我遇到了相同的错误: 错误代码 InvalidDigest:您指定的 Content-MD5 无效。 不知道我做错了什么? 感谢您的回复!
2个回答

4

好的,迈克尔的评论让我找到了正确的方向。经过反复阅读,我终于理解了他想告诉我的内容。对于其他仍然困惑的人,我尝试解释一下:

像“28e01cf6608332ae51d63af3364d77f2”这样的md5字符串是16字节摘要的十六进制表示形式。也就是说,这32个字符中的每2个字符代表1个字节。

28是第一个字节,e0是第二个字节,1c是第三个字节,以此类推,直到你有16个字节。

content-md5头部期望的是base64编码的16字节,而不是十六进制表示形式。否则,这种方法就会非常方便。

[FileHash md5HashOfFileAtPath:tempFilePath]

该方法只返回此十六进制表示法的NSString。因此,在进行字符串转换之前,我可以挖掘提取字节,或将字符串重新转换为NSData。

我选择了后者,使用我在这里找到的代码片段:

//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;

这个base64encodedString终于返回了一个成功的响应!感谢Michael!

我已经将md5字符串添加到S3PutObjectRequest中,如下所示: s3PutObjectRequest.contentMD5 = base64EncodedString; s3PutObjectRequest.httpMethod = @“PUT”; 但是当我尝试从S3PutObjectResponse检索eTag时,我得到了错误代码:BadDigest,消息:指定的Content-MD5与我们收到的不匹配。是否有任何原因? 我已经在这里发布了我的问题,请添加评论,如果您对此原因有任何想法 http://stackoverflow.com/questions/29847564/request-checksum-from-amazon-s3-in-ios - Mr.G

4
你需要对MD5哈希的二进制表示进行base64编码...而不是十六进制表示,这可能是你正在做的事情。
如果正确编码,结果值将在24个字符左右...如果编码不正确,则会是这个长度的两倍。

非常感谢您的帮助,Michael!我创建了一个NSData对象,如下所示:[md5 dataUsingEncoding:NSUTF8StringEncoding] --这不是md5的二进制表示吗?然后我将该NSData对象编码为base64。这样做有问题吗? - marimba
Objective C 不是我的专长领域。我熟悉 S3,并且以前已经实现过正确计算 Content-MD5 的代码。你所描述的 md5 与 etag 相同的情况让我怀疑你的起始值是正确的,因此最可能的问题是你从错误的起始点开始进行 base64 编码。能否提供一个由你的代码计算出来的 md5 校验和及其对应的 b64 等效值的示例呢?我会看看我得出的值是什么。 - Michael - sqlbot
md5 值为:"28e01cf6608332ae51d63af3364d77f2"(本地和作为 S3 上的 eTag)。得到的 base64 是 "MjhlMDFjZjY2MDgzMzJhZTUxZDYzYWYzMzY0ZDc3ZjI=" - 48 个字符,你是正确的!那么“正确”和“错误”的区别是什么? - marimba

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接