POSIX错误12(“不能分配内存”),而从iPhone上传文件

我正在开发一个iPhone应用程序,其中包括从相机上传完整照片(一般在1.5到2.0 MB之间)以及缩略图(小得多)到Amazon S3。

缩略图总是成功上传,但有时全部图像不会,而且当失败时,它们会以POSIX错误代码12,即ENOMEM失败。 但是,我添加了debugging代码来打印错误发生时的可用内存量,总是有相当多的空闲空间,通常超过100 MB。

此外,当上传发生在3G上的时候,这个错误会更频繁地出现,而当它上传到wifi上的时候,这种错误会更加频繁 – 这似乎很奇怪,因为请求没有被下载太多,上传的文件已经在内存中了stream媒体从磁盘没有改善)。

我已经尝试使用NSURLConnection,Foundation CFHTTP *函数和ASIHTTPRequest库来上传文件,但无论如何,错误都是以相同的频率发生的。 更奇怪的是,我的所有Googlesearch都显示,最终用户有时会从Safari获取错误代码12 – 我还没有看到任何iOS开发人员提到它。 我正在使用一个inheritance的代码库,所以有可能是有问题的,但我甚至不知道要找什么。 任何有识之士将不胜感激!

我能够解决此问题的唯一方法是直接使用套接字并手动形成HTTP标头。 所以我的上传代码目前看起来像这样:

- (void)socketClose { [_inputStream setDelegate:nil]; [_inputStream close]; [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; SCR_RELEASE_SAFELY(_inputStream); [_outputStream setDelegate:nil]; [_outputStream close]; [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; SCR_RELEASE_SAFELY(_outputStream); SCR_RELEASE_SAFELY(_headerBuffer); } - (void)sendRequest { [self socketClose]; SCR_RELEASE_SAFELY(_headerBuffer); if (!_shouldCancel) { NSString *httpMessage = [NSString stringWithFormat:@"POST upload.php HTTP/1.1\r\n" "Host:" #ifndef TESTBED " %@" #endif "\r\n" "User-Agent: MyApp/3.0.0 CFNetwork/534 Darwin/10.7.0\r\n" "Content-Length: %d\r\n" "Accept: */*\r\n" "Accept-Language: en-us\r\n" "Accept-Encoding: gzip, deflate\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Connection: keep-alive\r\n\r\n" "data=" #ifndef TESTBED , [self.serverUrl host] #endif , _bytesToUpload]; NSString *key = @"data="; NSData *keyData = [key dataUsingEncoding:NSASCIIStringEncoding]; _bytesToUpload -= [keyData length]; _bytesToUpload = MAX(0, _bytesToUpload); _headerBuffer = [[NSMutableData alloc] initWithData:[httpMessage dataUsingEncoding:NSUTF8StringEncoding]]; _writtenDataBytes = 0; CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault , (CFStringRef)[self.serverUrl host] #ifdef TESTBED , 8888 #else , 80 #endif , (CFReadStreamRef *)(&_inputStream) , (CFWriteStreamRef *)(&_outputStream)); [_inputStream setDelegate:self]; [_outputStream setDelegate:self]; [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_inputStream open]; [_outputStream open]; } } - (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { if (_outputStream == theStream) { switch (streamEvent) { case NSStreamEventOpenCompleted: { [self regenerateTimeoutTimer]; break; } case NSStreamEventHasSpaceAvailable: { SCR_RELEASE_TIMER(_timeoutTimer); NSInteger length = _headerBuffer.length; if (length > 0) { NSInteger written = [_outputStream write:(const uint8_t *)[_headerBuffer bytes] maxLength:length]; NSInteger rest = length - written; if (rest > 0) { memmove([_headerBuffer mutableBytes], (const uint8_t *)[_headerBuffer mutableBytes] + written, rest); } [_headerBuffer setLength:rest]; } else { const uint8_t *dataBytes = [_data bytes]; while ([_outputStream hasSpaceAvailable] && (_writtenDataBytes < _bytesToUpload)) { NSInteger written = [_outputStream write:dataBytes maxLength:MIN(_dataLength, _bytesToUpload - _writtenDataBytes)]; if (written > 0) { _writtenDataBytes += written; } } } [self regenerateTimeoutTimer]; break; } case NSStreamEventErrorOccurred: { SCR_RELEASE_TIMER(_timeoutTimer); [self reportError:[theStream streamError]]; break; } case NSStreamEventEndEncountered: { SCR_RELEASE_TIMER(_timeoutTimer); [self socketClose]; break; } } } else if (_inputStream == theStream) { switch (streamEvent) { case NSStreamEventHasBytesAvailable: { SCR_RELEASE_TIMER(_timeoutTimer); /* Read server response here if you wish */ [self socketClose]; break; } case NSStreamEventErrorOccurred: { SCR_RELEASE_TIMER(_timeoutTimer); [self reportError:[theStream streamError]]; break; } case NSStreamEventEndEncountered: { SCR_RELEASE_TIMER(_timeoutTimer); [self socketClose]; break; } } } } 

虽然ASIHTTPRequest可以在这里工作,但是我们决定摆脱这种依赖关系,以便获得性能,并准确地把所有东西都控制在我们自己的控制之下。 你可以使用Wireshark工具来debugging这种事情。

解决此问题的关键是使用stream上载文件。 当使用NSMutableURLRequest时,可以使用类似于以下内容的方法来完成:

 NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:filePath]]; 

在使用ASIHTTPRequest时,stream式传输文件是通过以下方式完成的:

 ASIHTTPRequest* request = [ASIHTTPRequest requestWithURL:url]; [request setPostBodyFilePath:filePath]; rRequest.shouldStreamPostDataFromDisk = YES; 

使用@autorelease{}为主函数使用请求操作( NSMutableUrlConnection )解决了此错误。 NSPOXIS有时只出现。

 - (void)main NSURLConnection* connection; @autoreleasepool //urgently needed for 3G upload { self.currentRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"test.php"]]; [self.currentRequest setHTTPMethod:@"PUT"]; [self.currentRequest setHTTPBody:self.data];//inpustStream doesn't work connection = [NSURLConnection connectionWithRequest:self.currentRequest delegate:self]; [connection start]; }//end autorelease pool do { [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]; if ([self isCancelled]) { connection = nil; isFailed = YES; break; } self.status(statusUpdateMessage); } while (!isFailed && !isCompleted); [timer invalidate];//test timer = nil; //corresponding of status via blocks self.completed(!isFailed); self.status(isFailed ? errorMessage : @"Completed"); if (isFailed) { self.failed(errorMessage != nil ? errorMessage : @"Undefined error"); } self.data = nil; self.currentRequest = nil; connection = nil; } 
Interesting Posts