NSMutableURLRequest 和内存问题

发布于 2024-12-10 00:46:53 字数 4053 浏览 0 评论 0原文

早上好, 我正在开发一个上传一些文件的应用程序;没有真正的问题,但只有一个大问题:如果我尝试上传 1GB 的文件,它会分配 1GB 的内存,你可以理解这不是一件好事。

这是我的代码:我将数据分配为 NSData,然后使用 NSMutableURLRequest + NSURLConnection 上传它们;我的问题是:有什么方法可以做到这一点而不需要分配整个内存?我已经搜索过了,但什么也没找到...

_uploadDataFile 是之前分配的 NSData 子类实例。

NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://my.server/page"]
                                                            cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                        timeoutInterval:60.0] autorelease];

NSData *dataFile;

if (_uploadDataFile == nil) {
    NSError *error = nil;
    dataFile = [[[NSData alloc] initWithContentsOfFile:@"pathToFile" options:NSDataReadingUncached error:&error] autorelease];

    if (error) {
        NSLog(@"Error %i: %@", [error code], [error localizedDescription]);
        [self fermaUpload]; //Stop Upload
        return;
    }
}

else 
    dataFile = [_uploadDataFile file];

NSMutableDictionary *dictPost = [[[NSMutableDictionary alloc] init] autorelease];
[dictPost setValue:dataFile forKey:@"uploads[]"];

NSData *mutData = [self dataForPOSTWithDictionary:dictPost boundary:@"0194784892923" nomeFile:[@"pathToFile" lastPathComponent]];

NSString *postLength = [NSString stringWithFormat:@"%d", [mutData length]];

[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"multipart/form-data; boundary=0194784892923" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:mutData];
[request setHTTPShouldHandleCookies:YES];

theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

这是 - (NSData *)dataForPOSTWithDictionary:boundary:nomeFile: 方法的样子:

- (NSData *)dataForPOSTWithDictionary:(NSDictionary *)aDictionary boundary:(NSString *)aBoundary nomeFile:(NSString *)nomeFile {
NSArray *myDictKeys = [aDictionary allKeys];
NSMutableData *myData = [NSMutableData dataWithCapacity:1];
NSString *myBoundary = [NSString stringWithFormat:@"--%@\r\n", aBoundary];

for(int i = 0;i < [myDictKeys count];i++) {
    id myValue = [aDictionary valueForKey:[myDictKeys objectAtIndex:i]];
    [myData appendData:[myBoundary dataUsingEncoding:NSUTF8StringEncoding]];
    //if ([myValue class] == [NSString class]) {
    if ([myValue isKindOfClass:[NSString class]]) {
        [myData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", [myDictKeys objectAtIndex:i]] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[[NSString stringWithFormat:@"%@", myValue] dataUsingEncoding:NSUTF8StringEncoding]];
    } else if(([myValue isKindOfClass:[NSURL class]]) && ([myValue isFileURL])) {
        [myData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [myDictKeys objectAtIndex:i], [[myValue path] lastPathComponent]] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[NSData dataWithContentsOfFile:[myValue path]]];
    } else if(([myValue isKindOfClass:[NSData class]])) {
        [myData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [myDictKeys objectAtIndex:i], nomeFile] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:myValue];
    } // eof if()

    [myData appendData:[[NSString stringWithString:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
} // eof for()
[myData appendData:[[NSString stringWithFormat:@"--%@--\r\n", aBoundary] dataUsingEncoding:NSUTF8StringEncoding]];

return myData; } // eof dataForPOSTWithDictionary:boundary:

谢谢你,对不起我的英语。

Good morning,
I'm developing an application that uploads some files; there are no real issues, but only one big problem: if I try to upload 1gb of files it allocates 1gb of memory, and you can understand this is not a good thing.

Here's my code: I allocate the data as NSData and then I use NSMutableURLRequest + NSURLConnection to upload them; my question is: are there any ways to do this without needing to allocate whole memory? I've searched out, but I found nothing...

_uploadDataFile is a NSData sub-class instance allocated before.

NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://my.server/page"]
                                                            cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                        timeoutInterval:60.0] autorelease];

NSData *dataFile;

if (_uploadDataFile == nil) {
    NSError *error = nil;
    dataFile = [[[NSData alloc] initWithContentsOfFile:@"pathToFile" options:NSDataReadingUncached error:&error] autorelease];

    if (error) {
        NSLog(@"Error %i: %@", [error code], [error localizedDescription]);
        [self fermaUpload]; //Stop Upload
        return;
    }
}

else 
    dataFile = [_uploadDataFile file];

NSMutableDictionary *dictPost = [[[NSMutableDictionary alloc] init] autorelease];
[dictPost setValue:dataFile forKey:@"uploads[]"];

NSData *mutData = [self dataForPOSTWithDictionary:dictPost boundary:@"0194784892923" nomeFile:[@"pathToFile" lastPathComponent]];

NSString *postLength = [NSString stringWithFormat:@"%d", [mutData length]];

[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"multipart/form-data; boundary=0194784892923" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:mutData];
[request setHTTPShouldHandleCookies:YES];

theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

and here's how - (NSData *)dataForPOSTWithDictionary:boundary:nomeFile: method looks like:

- (NSData *)dataForPOSTWithDictionary:(NSDictionary *)aDictionary boundary:(NSString *)aBoundary nomeFile:(NSString *)nomeFile {
NSArray *myDictKeys = [aDictionary allKeys];
NSMutableData *myData = [NSMutableData dataWithCapacity:1];
NSString *myBoundary = [NSString stringWithFormat:@"--%@\r\n", aBoundary];

for(int i = 0;i < [myDictKeys count];i++) {
    id myValue = [aDictionary valueForKey:[myDictKeys objectAtIndex:i]];
    [myData appendData:[myBoundary dataUsingEncoding:NSUTF8StringEncoding]];
    //if ([myValue class] == [NSString class]) {
    if ([myValue isKindOfClass:[NSString class]]) {
        [myData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", [myDictKeys objectAtIndex:i]] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[[NSString stringWithFormat:@"%@", myValue] dataUsingEncoding:NSUTF8StringEncoding]];
    } else if(([myValue isKindOfClass:[NSURL class]]) && ([myValue isFileURL])) {
        [myData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [myDictKeys objectAtIndex:i], [[myValue path] lastPathComponent]] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[NSData dataWithContentsOfFile:[myValue path]]];
    } else if(([myValue isKindOfClass:[NSData class]])) {
        [myData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [myDictKeys objectAtIndex:i], nomeFile] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
        [myData appendData:myValue];
    } // eof if()

    [myData appendData:[[NSString stringWithString:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
} // eof for()
[myData appendData:[[NSString stringWithFormat:@"--%@--\r\n", aBoundary] dataUsingEncoding:NSUTF8StringEncoding]];

return myData; } // eof dataForPOSTWithDictionary:boundary:

Thank you and sorry for my english.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一身骄傲 2024-12-17 00:46:53

一个最有趣的问题!有两种方法可以将正文数据输入到 NSURLConnection/Request 中:

  • NSData
  • NSInputStream

不幸的是,这两种方法都不是您想要的开箱即用的,因为您想要上传一个文件+一些表单数据。理想情况下,您可以子类化 NSInputStream 以提供您需要的格式化数据。但是,我上次检查时,NSURLConnection 无法处理此类子类。相反,请执行以下操作:

  1. 使用 CFStreamCreateBoundPair() 创建一对流。这将返回一个 CFWriteStreamCFReadStream,它们分别免费桥接到 NSOutputStreamNSInputStream
  2. 自行设置 输出流的委托
  3. 作为带有输入流的 URL 请求上的
  4. -setHTTPBodyStream:启动连接

现在应该定期调用输出流的委托来请求数据。您需要输入 POST 数据,然后输入文件数据块。因此,整个文件永远不会同时完全存在于内存中。

A most interesting problem! There are two ways of feeding body data into NSURLConnection/Request:

  • NSData
  • NSInputStream

Unfortunately, neither does what you want out of the box, since you want to upload a file + some form data. Ideally, you could subclass NSInputStream to provide the data formatted as you require. However, the last time I checked, NSURLConnection is unable to handle such subclasses. Instead, do something like:

  1. Create a pair of streams using CFStreamCreateBoundPair(). This returns a CFWriteStream and CFReadStream, which are toll-free bridged to NSOutputStream and NSInputStream, respectively
  2. Set yourself up as the output stream's delegate
  3. -setHTTPBodyStream: on the URL request with the input stream
  4. Start the connection

The output stream's delegate should now be called periodically, requesting data. You need to feed in the POST data, followed by chunks of file data. Thus the entire file is never entirely in memory at once.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文