我目前正在开发一个必须将大文件(主要是电影/视频)上传到网络的应用程序。在阅读了我的内容之后,我采用了将电影转换为NSData的方法,然后将其作为NSURLConnection's
HTTPBody
包含在内。但是,在将电影(最初为ALAsset
)转换为NSData
时,我会收到内存警告,然后是后续崩溃。
我不知道如何上传这些类型的大文件,如果这些数据只会导致即时崩溃。我想到的一个解决方案是写入文件系统,然后直接从那里上传文件,但我无法找到有关如何实现此目的的任何信息。
这是我使用的相关代码。如果有什么我在这里做错了,我很想知道。
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);
答案 0 :(得分:2)
假设以原生格式上传电影是有意义的,你可以使用BSD(即Unix)第3节界面轻松实现这一点:
给定一个filePath,打开文件并获取一个int文件描述符(fd)
使用fd,获取文件的长度
跟踪您已加载了多少,以便知道从何处获取更多数据
使用mmap(3)随时映射您要上传的数据,并使用mmap返回的void *指针作为数据的位置
发送数据后,将旧数据块映射并映射新块
发送完所有数据后,将最后一个块发送到目标,即关闭(fd)。
没有临时记忆 - 没有mallocs。每当我必须处理大文件时,我都会使用mmap。
编辑:您还可以使用NSData dataWithContentsOfFile:options,其中选项设置为使用mmap。然后,您可以根据需要使用字节指针读取小块。
答案 1 :(得分:1)
如果有人来到这里并且无法解决您的问题,我想出了一种方法来做到这一点。
您必须先将ALAssetRepresentation
写入磁盘(如here所述):
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;
然后你必须创建一个NSData对象,它可以在不使用内存资源的情况下映射磁盘(有点像David的答案,但受到this answer的启发):
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 ++类来处理套接字和连接
答案 2 :(得分:0)
你可能不应该试图一次性阅读整个资产:
Byte *buffer = (Byte *)malloc([representation size]);
NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil];
相反,设置一个循环并从块中读取资产。我概述了基本方法。你需要填补一些空白,但它应该解决内存问题。
您可能还想考虑在一个线程中运行它,这样就不会锁定UI。
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