I\'m working on an iPhone application that involves uploading full photos from the camera (generally between 1.5 to 2.0 MB each) as well as their thumbnails (much smaller) to Am
Have resolved this error with using operation for request (NSMutableUrlConnection
) with @autorelease{}
for main function.
NSPOXIS appears only sometimes.
- (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;
}
The only way I was able to work around this issue, is using sockets directly and forming HTTP header manually. So my uploading code currently looks like this:
- (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;
}
}
}
}
Although ASIHTTPRequest could work here, we decided to walk away from such dependencies both in order to get performance and to keep everything under our own control accurately. You can use Wireshark tool in order to debug this kind of things.
The key to getting around this issue is to upload the file using a stream. When using NSMutableURLRequest, this can be accomplished using something similar to the following:
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:filePath]];
When using ASIHTTPRequest, streaming a file is accomplished with this:
ASIHTTPRequest* request = [ASIHTTPRequest requestWithURL:url];
[request setPostBodyFilePath:filePath];
rRequest.shouldStreamPostDataFromDisk = YES;