2014-01-06 41 views
4

this issue类似。AFNetworking 2.0多部分请求主体空白

使用AFNetworking 2.0.3并尝试使用AFHTTPSessionManager的POST + constructBodyWithBlock上传图像。出于未知原因,看起来HTTP请求正文向服务器发出时总是空白。

我继承AFHTTPSessionManager下面(的[self POST ...]因此使用

我试过,建立请求两种方式

方法1:我只是想通过PARAMS,然后只添加图片数据应该存在它

- (void) createNewAccount:(NSString *)nickname accountType:(NSInteger)accountType primaryPhoto:(UIImage *)primaryPhoto 
{ 
    NSString *accessToken = self.accessToken; 

    // Ensure none of the params are nil, otherwise it'll mess up our dictionary 
    if (!nickname) nickname = @""; 
    if (!accessToken) accessToken = @""; 

    NSDictionary *params = @{@"nickname": nickname, 
          @"type": [[NSNumber alloc] initWithInteger:accountType], 
          @"access_token": accessToken}; 
    NSLog(@"Creating new account %@", params); 

    [self POST:@"accounts" parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { 
     if (primaryPhoto) { 
      [formData appendPartWithFileData:UIImageJPEGRepresentation(primaryPhoto, 1.0) 
             name:@"primary_photo" 
            fileName:@"image.jpg" 
            mimeType:@"image/jpeg"]; 
     } 
    } success:^(NSURLSessionDataTask *task, id responseObject) { 
     NSLog(@"Created new account successfully"); 
    } failure:^(NSURLSessionDataTask *task, NSError *error) { 
     NSLog(@"Error: couldn't create new account: %@", error); 
    }]; 
} 

方法2:试图在块本身来构建的形式数据:

- (void) createNewAccount:(NSString *)nickname accountType:(NSInteger)accountType primaryPhoto:(UIImage *)primaryPhoto 
{ 
    // Ensure none of the params are nil, otherwise it'll mess up our dictionary 
    if (!nickname) nickname = @""; 
    NSLog(@"Creating new account %@", params); 

    [self POST:@"accounts" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { 
     [formData appendPartWithFormData:[nickname dataUsingEncoding:NSUTF8StringEncoding] name:@"nickname"]; 
     [formData appendPartWithFormData:[NSData dataWithBytes:&accountType length:sizeof(accountType)] name:@"type"]; 
     if (self.accessToken) 
      [formData appendPartWithFormData:[self.accessToken dataUsingEncoding:NSUTF8StringEncoding] name:@"access_token"]; 
     if (primaryPhoto) { 
      [formData appendPartWithFileData:UIImageJPEGRepresentation(primaryPhoto, 1.0) 
             name:@"primary_photo" 
            fileName:@"image.jpg" 
            mimeType:@"image/jpeg"]; 
     } 
    } success:^(NSURLSessionDataTask *task, id responseObject) { 
     NSLog(@"Created new account successfully"); 
    } failure:^(NSURLSessionDataTask *task, NSError *error) { 
     NSLog(@"Error: couldn't create new account: %@", error); 
    }]; 
} 

使用任何一种方法,当HTTP请求命中服务器时,没有POST数据或查询字符串参数,只有HTTP头。

Transfer-Encoding: Chunked 
Content-Length: 
User-Agent: MyApp/1.0 (iPhone Simulator; iOS 7.0.3; Scale/2.00) 
Connection: keep-alive 
Host: 127.0.0.1:5000 
Accept: */* 
Accept-Language: en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5 
Content-Type: multipart/form-data; boundary=Boundary+0xAbCdEfGbOuNdArY 
Accept-Encoding: gzip, deflate 

有什么想法?还在AFNetworking's github repo上发布了一个bug。

+1

我用'AFHTTPRequestOperationManager'创建了multipart请求,它工作正常,但是当我使用几乎相同的'AFHTTPSessionManager'时,它失败了。看起来像'AFHTTPSessionManager'里有什么不妥之处。 – Rob

+0

这就是@Rob的绝招,现在让我们开放这个问题,但这绝对是一种臭虫。 –

+0

你有没有试过看'task.response'? Mine正在返回一个406的statusCode(但是同样的请求在AFHTTPRequestOperationManager中没有问题)。 – Rob

回答

4

挖掘到该进一步的,看来当结合使用NSURLSessionsetHTTPBodyStream,即使请求设置Content-Length(其AFURLRequestSerialization确实在requestByFinalizingMultipartFormData),该头是没有得到发送。您可以通过比较任务originalRequestcurrentRequestallHTTPHeaderFields进行确认。我也向查尔斯证实了这一点。

有趣的是,Transfer-Encoding设置为chunked(当长度未知时,这通常是正确的)。

底线,这似乎是AFNetworking的选择使用setHTTPBodyStream,而不是setHTTPBody的体现(不从这种行为遭受),其中,在与NSURLSession导致畸形请求的这种行为相结合。

我认为这与AFNetworking issue 1398有关。

8

罗布是绝对正确的,你看到的问题是关系到(现在关闭)issue 1398。但是,我想提供一个快速tl;博士以防其他人在寻找。

首先,这里由gberginc on github提供的代码片段,您可以在您的文件上传模式:

NSString* apiUrl = @"http://example.com/upload"; 

// Prepare a temporary file to store the multipart request prior to sending it to the server due to an alleged 
// bug in NSURLSessionTask. 
NSString* tmpFilename = [NSString stringWithFormat:@"%f", [NSDate timeIntervalSinceReferenceDate]]; 
NSURL* tmpFileUrl = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpFilename]]; 

// Create a multipart form request. 
NSMutableURLRequest *multipartRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" 
                            URLString:apiUrl 
                            parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) 
             { 
              [formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] 
                     name:@"file" 
                    fileName:fileName 
                    mimeType:@"image/jpeg" error:nil]; 
             } error:nil]; 

// Dump multipart request into the temporary file. 
[[AFHTTPRequestSerializer serializer] requestWithMultipartFormRequest:multipartRequest 
              writingStreamContentsToFile:tmpFileUrl 
                completionHandler:^(NSError *error) { 
                 // Once the multipart form is serialized into a temporary file, we can initialize 
                 // the actual HTTP request using session manager. 

                 // Create default session manager. 
                 AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; 

                 // Show progress. 
                 NSProgress *progress = nil; 
                 // Here note that we are submitting the initial multipart request. We are, however, 
                 // forcing the body stream to be read from the temporary file. 
                 NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:multipartRequest 
                                fromFile:tmpFileUrl 
                                progress:&progress 
                              completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) 
                           { 
                            // Cleanup: remove temporary file. 
                            [[NSFileManager defaultManager] removeItemAtURL:tmpFileUrl error:nil]; 

                            // Do something with the result. 
                            if (error) { 
                             NSLog(@"Error: %@", error); 
                            } else { 
                             NSLog(@"Success: %@", responseObject); 
                            } 
                           }]; 

                 // Add the observer monitoring the upload progress. 
                 [progress addObserver:self 
                    forKeyPath:@"fractionCompleted" 
                     options:NSKeyValueObservingOptionNew 
                     context:NULL]; 

                 // Start the file upload. 
                 [uploadTask resume]; 
                }]; 

其次,总结问题(为什么你必须使用一个临时文件作为解决办法),它确实是两倍。

  1. 苹果认为内容长度报头是在其控制下,并且当HTTP体流被设置为一个NSURLRequest苹果的库将设置编码分块,然后弃该头(从而清除任何内容 - 长度值AFNetworking套)
  2. 上传就被打不支持Transfer-Encoding: Chunked(如S3)服务器

但事实证明,如果你从上传文件的请求(因为总的要求大小是提前知道的),Apple的库会正确设置内容长度标题。疯了吧?

0

我跑了这个问题我自己,并试图两种方法,在这里建议的方法...

事实证明,这是为改变附加数据“姓名”键“文件”,而不是简单的文件名变量。

确保您的数据密钥匹配,否则您将看到空数据集的相同症状。