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
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.