使用NsurlConnection下载大型base64encoded数据

时间:2016-05-26 09:17:15

标签: ios objective-c nsurlconnection

我正在尝试使用 POST 请求从服务器下载大文件。该文件作为 base64Encoded 数据接收。文件大小超过400Mb。我使用的第一种方法是附加在 didReceiveData 中接收的数据,然后在 connectionDidFinishLoading 中附加我用于解码数据并写入数据到文件目录中的文件。此方法适用于小尺寸的文件。但它崩溃,因为文件大小非常大(400MB)。然后我使用NSFileHandle的方法在接收数据时以块的形式写入文件。我用3MB的块来写数据。对于小于3MB的文件,从base64直接解码接收的数据,并成功创建文件。但是当文件大小超过3MB并且我尝试使用“ initWithBase64EncodedData:Options ”从base64解码块数据时,我得到的数据是nil。想知道我在代码中做错了什么。

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
    receivedData = [[NSMutableData alloc] init];

    NSString* folderPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Documents/UserDocs"];
    if(![[NSFileManager defaultManager]  fileExistsAtPath:folderPath]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:folderPath withIntermediateDirectories:NO attributes:nil error:nil];
   }

NSString *strFilePath = [folderPath stringByAppendingPathComponent:strFileName];    NSString *newFilePath = [NSString stringWithFormat:@"%@.%@", strFilePath, strFileExtension];
if(![[NSFileManager defaultManager]  fileExistsAtPath:newFilePath]) {
          [[NSFileManager defaultManager] createFileAtPath:newFilePath contents:nil attributes:nil];
        }

self.fileHandle = [[NSFileHandle fileHandleForWritingAtPath:newFilePath] retain];
        [self.fileHandle seekToFileOffset:0];
}

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
    [receivedData appendData:data];
    NSUInteger chunkSize = 300000;
    if( receivedData.length > chunkSize  && self.fileHandle!=nil) {

        NSData *chunk = [receivedData subdataWithRange:NSMakeRange(0, chunkSize)];
        NSData *writableData = [[NSData alloc] initWithBase64EncodedData:chunk options:NSDataBase64DecodingIgnoreUnknownCharacters];
       [receivedData replaceBytesInRange:NSMakeRange(0, chunkSize) withBytes:NULL length:0];

       [self.fileHandle seekToEndOfFile];
       [self.fileHandle writeData:writableData];
       [writableData release];
    }
}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
    if (receivedData)
    {
        NSData *writableData = [[NSData alloc] initWithBase64EncodedData:receivedData options:NSDataBase64DecodingIgnoreUnknownCharacters];
        [receivedData release];

        [self.fileHandle seekToEndOfFile];
        [self.fileHandle writeData:writableData];
        [writableData release];

        NSLog(@"File Write Success!!");
    }
}

1 个答案:

答案 0 :(得分:0)

两种可能的方法:

  1. 跳过base64编码(如果可以的话)。
  2. 在获取数据时将数据写入磁盘,然后使用内存映射将文件映射到RAM中,您可以在其中使用它,就好像它在内存中一样,但实际上没有占用内存。
  3. 基本上:

    #include <sys/stat.h>
    #include <sys/mman.h>
    ...
    NSURL *url = // path to file on disk
    char *path = [url fileSystemRepresentation];
    struct stat buf;
    
    // Round up to an exact multiple of the page size
    NSUInteger size = (buf.st_size + PAGE_MASK) & ~PAGE_MASK;
    NSData *data = nil;
    
    if (!stat(path, &buf)) {
        int fd = open(path, O_RDONLY);
        if (fd != -1) {
            mmap(NULL, size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
            data = [NSData dataWithBytesNoCopy:buf length:buf.st_size];
        }
    }
    
    // Perform a base64 decode on the NSData object and hope that
    // at least the result will fit in RAM.
    
    // Clean up.
    data = nil;
    if (!stat(path, &buf)) {
        int fd = open(path, O_RDONLY);
        if (fd != -1) {
            munmap(addr, size);
        }
    }
    

    创建可以将数据直接写入磁盘上文件的base64解码器留给读者练习。 : - )