将大型NSData上传到Web

时间:2012-08-14 22:07:23

标签: iphone ios nsurlconnection

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

3 个答案:

答案 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