iOS计算大文件的MD5

·  阅读 4057

iOS计算MD5的方式有很多种:

1. 常见的方式

#include <CommonCrypto/CommonDigest.h>
/// 计算文件的MD5
- (NSString *)getFileMD5StrFromPath1:(NSString *)path {
	
	NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
	if( handle== nil ) return @""; // 如果文件不存在
	
	CC_MD5_CTX md5;
	
	CC_MD5_Init(&md5);
	
	BOOL done = NO;
	while(!done)
		{
		NSData* fileData = [handle readDataOfLength:256];
		CC_MD5_Update(&md5, [fileData bytes], [fileData length]);
		if( [fileData length] == 0 ) done = YES;
		}
	unsigned char digest[CC_MD5_DIGEST_LENGTH];
	CC_MD5_Final(digest, &md5);
	NSString* s = [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
				   digest[0], digest[1],
				   digest[2], digest[3],
				   digest[4], digest[5],
				   digest[6], digest[7],
				   digest[8], digest[9],
				   digest[10], digest[11],
				   digest[12], digest[13],
				   digest[14], digest[15]];
	return s;
}
复制代码

这种方式在计算文件较小的时候能够返回正确的值,
但是在遇到大文件(比如超过1G,具体的临界值没有确定)会出现应用闪退现象。

第二种方式

/// 获取文件的MD5值,如果文件不存在直接返回空字符串
- (NSString *)getFileMD5StrFromPath2:(NSString *)path {
	NSFileManager *fileManager = [NSFileManager defaultManager];
	
	if([fileManager fileExistsAtPath:path isDirectory:nil]) {
		
		NSData *data = [NSData dataWithContentsOfFile:path];
		
		unsigned char digest[CC_MD5_DIGEST_LENGTH];
		
		CC_MD5( data.bytes, (CC_LONG)data.length, digest );
		
		NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
		
		for( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++ ) {
			[output appendFormat:@"%02x", digest[i]];
		}
		return output;
	} else {
		return @"";
	}
}
复制代码

这种方式在计算文件较小的时候能够返回正确的MD5值,在计算大文件MD5值的时候,应用不会闪退但是会返回错误的MD5:"d41d8cd98f00b204e9800998ecf8427e",在MD5解密的网站进行解密的时候,返回结果是空字符串。

查询结果:
[空密码]/[Empty String]
复制代码

第三种方式,可以计算大文件件的MD5

//首先声明一个宏定义
#define FileHashDefaultChunkSizeForReadingData 1024*8
CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath,
                                       size_t chunkSizeForReadingData) {
    
    // Declare needed variables
    CFStringRef result = NULL;
    CFReadStreamRef readStream = NULL;
    
    // Get the file URL
    CFURLRef fileURL =
    CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
                                  (CFStringRef)filePath,
                                  kCFURLPOSIXPathStyle,
                                  (Boolean)false);
    if (!fileURL) goto done;
    
    // Create and open the read stream
    readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault,
                                            (CFURLRef)fileURL);
    if (!readStream) goto done;
    bool didSucceed = (bool)CFReadStreamOpen(readStream);
    if (!didSucceed) goto done;
    
    // Initialize the hash object
    CC_MD5_CTX hashObject;
    CC_MD5_Init(&hashObject);
    
    // Make sure chunkSizeForReadingData is valid
    if (!chunkSizeForReadingData) {
        chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData;
    }
    
    // Feed the data to the hash object
    bool hasMoreData = true;
    while (hasMoreData) {
        uint8_t buffer[chunkSizeForReadingData];
        CFIndex readBytesCount = CFReadStreamRead(readStream,
                                                  (UInt8 *)buffer,
                                                  (CFIndex)sizeof(buffer));
        if (readBytesCount == -1) break;
        if (readBytesCount == 0) {
            hasMoreData = false;
            continue;
        }
        CC_MD5_Update(&hashObject,(const void *)buffer,(CC_LONG)readBytesCount);
    }
    
    // Check if the read operation succeeded
    didSucceed = !hasMoreData;
    
    // Compute the hash digest
    unsigned char digest[CC_MD5_DIGEST_LENGTH];
    CC_MD5_Final(digest, &hashObject);
    
    // Abort if the read operation failed
    if (!didSucceed) goto done;
    
    // Compute the string result
    char hash[2 * sizeof(digest) + 1];
    for (size_t i = 0; i < sizeof(digest); ++i) {
        snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i]));
    }
    result = CFStringCreateWithCString(kCFAllocatorDefault,
                                       (const char *)hash,
                                       kCFStringEncodingUTF8);
    
done:
    
    if (readStream) {
        CFReadStreamClose(readStream);
        CFRelease(readStream);
    }
    if (fileURL) {
        CFRelease(fileURL);
    }
    return result;
}
/// 第二种方式计算大文件MD5
- (NSString*)getBigfileMD5:(NSString*)path
{
    return (__bridge_transfer NSString *)FileMD5HashCreateWithPath((__bridge CFStringRef)path, FileHashDefaultChunkSizeForReadingData);
}
复制代码

这种方式是最为严瑾的计算方法,可以计算小文件,计算大文件时耗时较长能够计算正确的MD5值,

总结:

  1. 第一种方式是直接返回值没有耗时操作但是可能会出现错误,、
  2. 第二种在计算大文件会闪退
  3. 能够完美计算所有文件的MD5,计算大文件会耗时(正常现象)
分类:
iOS
标签:
分类:
iOS
标签:
收藏成功!
已添加到「」, 点击更改