4

我目前正在开发一个必须将大文件(主要是电影/视频)上传到网络的应用程序。在阅读了我所能阅读的内容之后,我采用了将电影转换为 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);
4

3 回答 3

2

假设以原始格式上传电影是有意义的,您可以使用 BSD(即 Unix)第 3 节界面使这更容易:

  • 给定一个文件路径,打开文件并获取一个 int 文件描述符 (fd)

  • 用fd,获取文件的长度

  • 跟踪您加载了多少数据,以便您知道从哪里获取更多数据

  • 使用 mmap(3) 随时映射你要上传的数据,并使用 mmap 返回的 void * 指针作为数据的位置

  • 当数据已经发送,munmap 旧数据块并 mmap 一个新块

  • 发送完所有数据后,munmap 最后一个块,close(fd)。

没有临时内存 - 没有 malloc。每当我必须处理大文件时,我都会使用 mmap。

编辑:您还可以将 NSData dataWithContentsOfFile:options 与设置为使用 mmap 的选项一起使用。然后,您将根据需要使用字节指针来读取小块。

于 2012-08-14T23:22:46.330 回答
1

万一有人到了这里并且无法解决您的问题,我想出了一种方法来做到这一点。您必须首先将您的磁盘写入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;

然后,您必须创建一个 NSData 对象,该对象可以在不使用内存资源的情况下映射磁盘(有点像大卫的回答,但受此回答的启发):

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++ 类来处理套接字和连接

于 2013-04-23T12:49:14.223 回答
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
于 2012-08-14T22:34:28.627 回答