将大型NSData上传到Web
我目前正在开发一个应用程序,它需要将大文件(主要是电影/video)上传到networking上。 看完我所能做的之后,我将电影转换为NSData,然后将其作为NSURLConnection's
HTTPBody
。 但是,在将电影(最初是一个ALAsset
)转换为NSData
,我收到内存警告,然后再发生崩溃。
我不知道如何去上传这些types的大文件,如果这些数据只是造成即时崩溃。 我正在考虑的一个解决scheme是写入文件系统,然后直接从那里上传文件,但是我一直没能find任何信息来完成这个任务。
这是我使用的相关代码。 如果有什么我在这里做错了,我很想知道。
ALAssetRepresentation *representation = [asset defaultRepresentation]; Byte *buffer = (Byte *)malloc([representation size]); NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil]; uploadData = [NSData dataWithBytes:buffer length:buffered]; free(buffer);
假设以本地格式上传电影是有意义的,那么使用BSD(即Unix)第3节介面可以更容易地实现:
-
给一个文件path,打开文件并获得一个int文件描述符(fd)
-
用fd,得到文件的长度
-
跟踪你已经加载了多less,所以你知道从哪里获得更多的数据
-
使用mmap(3)来随时映射想要上传的数据,并使用mmap返回的void *指针作为数据的位置
-
当数据已经发送时,munmap旧数据块和mmap一个新的块
-
毕竟数据发送后,munmap最后一个块,closures(fd)。
没有临时记忆 – 没有mallocs。 每当我必须处理大文件时,我都会使用mmap。
编辑:你也可以使用NSData的dataWithContentsOfFile:选项设置为使用mmap。 然后,您将使用字节指针来读取小块,因为你需要它们。
如果有人来到这里,不能解决你的问题,我想出了一个办法做到这一点。 您必须先将ALAssetRepresentation
写入磁盘(如下所述):
NSUInteger chunkSize = 100 * 1024; NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp.tmp"]; uint8_t *chunkBuffer = malloc(chunkSize * sizeof(uint8_t)); NSUInteger length = [rep size]; NSFileHandle *fileHandle = [[NSFileHandle fileHandleForWritingAtPath: tempFile] retain]; if(fileHandle == nil) { [[NSFileManager defaultManager] createFileAtPath:tempFile contents:nil attributes:nil]; fileHandle = [[NSFileHandle fileHandleForWritingAtPath:tempFile] retain]; } NSUInteger offset = 0; do { NSUInteger bytesCopied = [rep getBytes:chunkBuffer fromOffset:offset length:chunkSize error:nil]; offset += bytesCopied; NSData *data = [[NSData alloc] initWithBytes:chunkBuffer length:bytesCopied]; [fileHandle writeData:data]; [data release]; } while (offset < length); [fileHandle closeFile]; [fileHandle release]; free(chunkBuffer); chunkBuffer = NULL;
然后,你必须创build一个NSData对象,可以映射磁盘而不使用内存资源(有点像David的答案,但受到这个答案的启发):
NSError *error; NSData *fileData = [NSData dataWithContentsOfFile:tempFile options:NSDataReadingMappedIfSafe error:&error]; if (!fileData) { NSLog(@"Error %@ %@", error, [error description]); NSLog(@"%@", tempFile); //do what you need with the error }
编辑虽然,如果你正在上传文件的地方,你应该打开一个连接,并发送文件的小缓冲区,有点像我上面做的。 我不得不写一个C ++类来处理套接字和连接
你可能不应该试图一次性读取整个资产:
Byte *buffer = (Byte *)malloc([representation size]); NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil];
相反,build立一个循环,并从块中读取资产。 我已经概述了基本的方法。 你需要填补一些空白,但它应该解决内存问题。
你可能也想考虑在一个线程中运行这个,所以你不要lockingUI。
NSError error; int bufferSize = 1000; float offset=0.0; //TODO: Open Connection while (1) { Byte *buffer = (Byte *)malloc(bufferSize); NSUInteger buffered = [representation getBytes:buffer fromOffset:offset length:bufferSize error:&error]; //TODO: Write data //TODO: Increment offset, check errors free(buffer); //if (done){ //break; //} } //TODO close eonnection