从iPhone上传文件时出现POSIX错误12("无法分配内存")

Ale*_*aud 9 iphone http put ios

我正在开发一个iPhone应用程序,它涉及从相机上传完整张照片(通常每张介于1.5到2.0 MB之间)以及缩略图(小得多)上传到Amazon S3.

缩略图始终成功上传,但有时完整图像不会上传,当失败时,它们会失败并显示POSIX错误代码12,即ENOMEM.但是,我添加了调试代码来在错误发生时打印可用内存量,而且总是有一点免费,通常超过100 MB.

此外,当上传发生在3G上时,错误会更频繁地发生,而当它通过wifi发生时则更少 - 这看起来很奇怪,因为请求没有下载太多而且正在上传的文件已经在内存中了(我也试过了)从磁盘流式传输而没有任何改进).

我尝试使用NSURLConnection,Foundation CFHTTP*函数和ASIHTTPRequest库上传文件,但无论如何,错误都以相同的频率发生.甚至更奇怪的是,我所有的谷歌搜索都透露,最终用户有时会从Safari获得错误代码12 - 我还没有看到任何iOS开发者提到它.我正在使用继承的代码库,所以它可能有问题,但我甚至不确定要寻找什么.任何见解将不胜感激!

Ale*_* N. 2

我能够解决这个问题的唯一方法是直接使用套接字并手动形成 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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

尽管 ASIHTTPRequest 可以在这里工作,但我们决定放弃这种依赖关系,以便获得性能并让一切都在我们自己的准确控制之下。您可以使用 Wireshark 工具来调试此类事情。