2012-06-11 9 views
9

Estoy trabajando en la aplicación Objective C Cocoa. Probé CC_MD5 in CommonCrypto, y funcionó bien; sin embargo, cuando le di 5 archivos de gygabyte, toda mi computadora se congeló y se colgó. El algoritmo MD5 procesa la entrada como fragmentos de 512 bytes y realmente no requiere toda la entrada a la vez. ¿Hay una biblioteca en Objective C o C que solicite el siguiente fragmento de 512 bytes en lugar de tomar todas las entradas a la vez?¿Hay una biblioteca MD5 que no requiera toda la entrada al mismo tiempo?

Respuesta

11

hay una gran hilo en el cálculo de MD5 de archivos de gran tamaño en obj-C aquí: http://www.iphonedevsdk.com/forum/iphone-sdk-development/17659-calculating-md5-hash-large-file.html

Aquí está la solución a alguien se le ocurrió allí:

+(NSString*)fileMD5:(NSString*)path 
{ 
    NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path]; 
    if(handle== nil) return @"ERROR GETTING FILE MD5"; // file didnt exist 

    CC_MD5_CTX md5; 

    CC_MD5_Init(&md5); 

    BOOL done = NO; 
    while(!done) 
    { 
     NSAutoreleasePool * pool = [NSAutoreleasePool new]; 
     NSData* fileData = [handle readDataOfLength: CHUNK_SIZE ]; 
     CC_MD5_Update(&md5, [fileData bytes], [fileData length]); 
     if([fileData length] == 0) done = YES; 
       [pool drain]; 
    } 
    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; 
} 
+0

Gracias por el enlace, leyéndolo ahora mismo. –

+3

Esta respuesta se beneficiaría de una actualización para ARC. El código en el ciclo debe colocarse dentro de un bloque '@autoreleasepool {}'. –

2

CC_MD5() está diseñado para procesar toda su entrada a la vez. 5GB es probablemente más de lo que realmente puede almacenar en cualquier lugar. Para datos más grandes, CommonCrypto puede operar en trozos a la vez, si usa CC_MD5_CTX, CC_MD5_Init(), CC_MD5_Update() y CC_MD5_Final(). Consulte la documentación de CommonCrypto o Google para obtener más información y un código de ejemplo.

2

Aquí hay una mejor manera de hacerlo usando apis de envío, para mayor eficiencia. ¡Lo estoy usando en producción y está funcionando bien!

#import "CalculateMD5.h" 

// Cryptography 
#include <CommonCrypto/CommonDigest.h> 

@implementation CalculateMD5 

- (id)init 
{ 
    self = [super init]; 
    if (self) 
    { 
     MD5ChecksumOperationQueue = dispatch_queue_create("com.test.calculateMD5Checksum", DISPATCH_QUEUE_SERIAL); 
    } 
    return self; 
} 

- (void)closeReadChannel 
{ 
    dispatch_async(MD5ChecksumOperationQueue, ^{ 
     dispatch_io_close(readChannel, DISPATCH_IO_STOP); 
    }); 
} 

- (void)MD5Checksum:(NSString *)pathToFile TCB:(void(^)(NSString *md5, NSError *error))tcb 
{ 
    // Initialize the hash object 
    __block CC_MD5_CTX hashObject; 
    CC_MD5_Init(&hashObject); 

    readChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, 
               pathToFile.UTF8String, 
               O_RDONLY, 0, 
               MD5ChecksumOperationQueue, 
               ^(int error) { 
                [self closeReadChannel]; 
               }); 

    if (readChannel == nil) 
    { 
     NSError* e = [NSError errorWithDomain:@"MD5Error" 
             code:-999 userInfo:@{ 
        NSLocalizedDescriptionKey : @"failed to open file for calculating MD5." 
         }]; 
     tcb(nil, e); 
     return; 
    } 

    dispatch_io_set_high_water(readChannel, 512*1024); 

    dispatch_io_read(readChannel, 0, SIZE_MAX, MD5ChecksumOperationQueue, ^(bool done, dispatch_data_t data, int error) { 
     if (error != 0) 
     { 
      NSError* e = [NSError errorWithDomain:@"ExamSoftMD5" 
              code:error userInfo:@{ 
         NSLocalizedDescriptionKey : @"failed to read from file for calculating MD5." 
          }]; 
      tcb(nil, e); 
      [self closeReadChannel]; 
      return; 
     } 

     if (dispatch_data_get_size(data) > 0) 
     { 
      const void *buffer = NULL; 
      size_t size = 0; 
      data = dispatch_data_create_map(data, &buffer, &size); 

      CC_MD5_Update(&hashObject, (const void *)buffer, (CC_LONG)size); 
     } 

     if (done == YES) 
     { 
      // Compute the hash digest 
      unsigned char digest[CC_MD5_DIGEST_LENGTH]; 
      CC_MD5_Final(digest, &hashObject); 

      // Compute the string result 
      char *hash = calloc((2 * sizeof(digest) + 1), sizeof(char)); 
      for (size_t i = 0; i < sizeof(digest); ++i) 
      { 
       snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i])); 
      } 

      tcb(@(hash), nil); 

      [self closeReadChannel]; 
     } 
    }); 
} 


@end 
Cuestiones relacionadas