这是Asynchronously decrypt a large file with RNCryptor on iOS的后续

我设法用本文中描述的方法异步解密了一个较大的下载文件(60Mb),由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)文件或模拟器上的大型(60Mb)文件来说,这很好。但是在真正的iPad1上,由于内存限制,它崩溃了,因此很明显我无法在我的生产应用程序中保留此过程。

我觉得我需要通过使用dispatch_async将数据发送到解密器,而不是在while循环中盲目发送它,但是我完全迷失了。我试过了:
  • while之前创建自己的队列,并使用dispatch_async(myQueue, ^{ [decryptor addData:data]; });
  • 相同,但是在while循环
  • 内部调度整个代码
  • 相同,但调度整个while循环
  • 使用RNCryptor提供的responseQueue代替我自己的队列

  • 在这4个变体中,没有任何作用。

    我还没有对调度队列的完整了解。我觉得问题出在这里。我很高兴有人能对此有所启发。

    最佳答案

    如果您一次只想处理一个块,则仅在第一个块回叫您时才处理一个块。您不需要信号量就可以了,您只需要在回调内执行下一个读取。您可能需要在@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();
    

    08-26 03:18