这是使用RNCryptor在iOS上异步解密大文件的后续行动。
我用本文中描述的方法异步解密了一个大的下载文件(60 by ),Calman在他的回答中对此进行了更正。
基本上是这样的:
int blockSize = 32 * 1024;
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:...];
NSOutputStream *decryptedStream = [NSOutputStream output...];
[cryptedStream open];
[decryptedStream open];
RNDecryptor *decryptor = [[RNDecryptor alloc] initWithPassword:@"blah" handler:^(RNCryptor *cryptor, NSData *data) {
NSLog("Decryptor recevied %d bytes", data.length);
[decryptedStream write:data.bytes maxLength:data.length];
if (cryptor.isFinished) {
[decryptedStream close];
// call my delegate that I'm finished with decrypting
}
}];
while (cryptedStream.hasBytesAvailable) {
uint8_t buf[blockSize];
NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
NSData *data = [NSData dataWithBytes:buf length:bytesRead];
[decryptor addData:data];
NSLog("Sent %d bytes to decryptor", bytesRead);
}
[cryptedStream close];
[decryptor finish];然而,我仍然面临一个问题:在解密之前,整个数据被加载到内存中。我可以看到一堆“发送X字节到解密器”,在此之后,控制台中同样的“解密器回收X字节”,当我希望看到“发送、接收、发送、接收”时。
对于小文件(2Mb)或模拟器上的大文件(60 my )来说,这是可以的;但是在一个真实的iPad1上,它会因为内存限制而崩溃,所以显然我不能在我的生产应用程序中保留这个过程。
我觉得我需要使用dispatch_async将数据发送到解密器,而不是盲目地在while循环中发送数据,但是我完全迷路了。我试过:
while之前创建自己的队列,并使用dispatch_async(myQueue, ^{ [decryptor addData:data]; });while循环中分配整个代码while循环RNCryptor-provided responseQueue而不是我自己的队列在这4种变体中,没有什么起作用的。
我还没有完全理解调度队列,我觉得问题就在这里。如果有人能澄清这件事,我会很高兴的。
发布于 2013-01-29 15:21:02
如果一次只处理一个块,那么只在第一个块调用回您时处理一个块。您不需要一个信号量来完成这个任务,您只需要在回调中执行下一个读取。您可能需要@autoreleasepool块在readStreamBlock中,但我认为您不需要它。
当我有时间时,我可能会将它直接包装到RNCryptor中。我为此打开了Issue#47。我愿意撤回请求。
// Make sure that this number is larger than the header + 1 block.
// 33+16 bytes = 49 bytes. So it shouldn't be a problem.
int blockSize = 32 * 1024;
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:@"C++ Spec.pdf"];
NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:@"/tmp/C++.crypt" append:NO];
[cryptedStream open];
[decryptedStream open];
// We don't need to keep making new NSData objects. We can just use one repeatedly.
__block NSMutableData *data = [NSMutableData dataWithLength:blockSize];
__block RNEncryptor *decryptor = nil;
dispatch_block_t readStreamBlock = ^{
[data setLength:blockSize];
NSInteger bytesRead = [cryptedStream read:[data mutableBytes] maxLength:blockSize];
if (bytesRead < 0) {
// Throw an error
}
else if (bytesRead == 0) {
[decryptor finish];
}
else {
[data setLength:bytesRead];
[decryptor addData:data];
NSLog(@"Sent %ld bytes to decryptor", (unsigned long)bytesRead);
}
};
decryptor = [[RNEncryptor alloc] initWithSettings:kRNCryptorAES256Settings
password:@"blah"
handler:^(RNCryptor *cryptor, NSData *data) {
NSLog(@"Decryptor recevied %ld bytes", (unsigned long)data.length);
[decryptedStream write:data.bytes maxLength:data.length];
if (cryptor.isFinished) {
[decryptedStream close];
// call my delegate that I'm finished with decrypting
}
else {
// Might want to put this in a dispatch_async(), but I don't think you need it.
readStreamBlock();
}
}];
// Read the first block to kick things off
readStreamBlock();发布于 2013-01-28 21:47:31
西里尔
由于内存限制,应用程序崩溃的原因是RNCryptor缓冲区超出了设备的能力。
基本上,您读取文件内容的速度远远快于RNCryptor所能处理的速度。因为它不能足够快地解密,所以它会缓冲传入的流,直到它能够处理它。
我还没有时间深入研究RNCryptor代码,并弄清楚它是如何使用GCD来管理所有内容的,但是您可以使用信号量来强制读取等待上一个块被解密。
下面的代码可以成功地解密iPad 1上的225 an文件,而不会崩溃。
它有一些问题,我不太满意,但它应该给你一个不错的起点。
有些事情要注意:
就我个人而言,我认为必须有一个更好的解决方案,但我还没有时间来研究更多。
我希望这能帮到你。
- (IBAction)decryptWithSemaphore:(id)sender {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int total = 0;
int blockSize = 32 * 1024;
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *input = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.rncryptor"];
NSString *output = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.decrypted.pdf"];
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:input];
__block NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:output append:NO];
__block NSError *decryptionError = nil;
[cryptedStream open];
[decryptedStream open];
RNDecryptor *decryptor = [[RNDecryptor alloc] initWithPassword:@"12345678901234567890123456789012" handler:^(RNCryptor *cryptor, NSData *data) {
@autoreleasepool {
NSLog(@"Decryptor recevied %d bytes", data.length);
[decryptedStream write:data.bytes maxLength:data.length];
dispatch_semaphore_signal(semaphore);
data = nil;
if (cryptor.isFinished) {
[decryptedStream close];
decryptionError = cryptor.error;
// call my delegate that I'm finished with decrypting
}
}
}];
while (cryptedStream.hasBytesAvailable) {
@autoreleasepool {
uint8_t buf[blockSize];
NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
if (bytesRead > 0) {
NSData *data = [NSData dataWithBytes:buf length:bytesRead];
total = total + bytesRead;
[decryptor addData:data];
NSLog(@"New bytes to decryptor: %d Total: %d", bytesRead, total);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
}
}
[cryptedStream close];
[decryptor finish];
dispatch_release(semaphore);
}发布于 2013-04-24 02:00:28
在过去的两天试图让我的MBProgress hud用Calman的代码更新它的进度之后,我想出了以下内容。使用的内存仍然很低,用户界面也会更新。
- (IBAction)decryptWithSemaphore:(id)sender {
__block int total = 0;
int blockSize = 32 * 1024;
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *input = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.rncryptor"];
NSString *output = [[docPaths objectAtIndex:0] stringByAppendingPathComponent:@"zhuge.decrypted.pdf"];
NSInputStream *cryptedStream = [NSInputStream inputStreamWithFileAtPath:input];
__block NSOutputStream *decryptedStream = [NSOutputStream outputStreamToFileAtPath:output append:NO];
__block NSError *decryptionError = nil;
__block RNDecryptor *encryptor=nil;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:input error:NULL];
__block long long fileSize = [attributes fileSize];
[cryptedStream open];
[decryptedStream open];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
encryptor = [[RNDecryptor alloc] initWithPassword:@"12345678901234567890123456789012" handler:^(RNCryptor *cryptor, NSData *data) {
@autoreleasepool {
NSLog(@"Decryptor recevied %d bytes", data.length);
[decryptedStream write:data.bytes maxLength:data.length];
dispatch_semaphore_signal(semaphore);
data = nil;
if (cryptor.isFinished) {
[decryptedStream close];
decryptionError = cryptor.error;
[cryptedStream close];
[encryptor finish];
// call my delegate that I'm finished with decrypting
}
}
}];
while (cryptedStream.hasBytesAvailable) {
@autoreleasepool {
uint8_t buf[blockSize];
NSUInteger bytesRead = [cryptedStream read:buf maxLength:blockSize];
if (bytesRead > 0) {
NSData *data = [NSData dataWithBytes:buf length:bytesRead];
total = total + bytesRead;
[encryptor addData:data];
NSLog(@"New bytes to decryptor: %d Total: %d", bytesRead, total);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
HUD.progress = (float)total/fileSize;
});
}
}
}
});}
https://stackoverflow.com/questions/14481605
复制相似问题