AFNetworking 2.0多部分请求主体空白
类似于这个问题 。
使用AFNetworking 2.0.3并尝试使用AFHTTPSessionManager的POST + constructBodyWithBlock上传图像。 由于未知的原因,似乎HTTP请求正文向服务器发送时总是空白。
我在下面的子类AFHTTPSessionManager(因此使用[self POST ...]
。
我试过两种方式来构build请求。
方法1 :我只是试图通过参数,然后添加图像数据,如果它存在。
- (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 :试图在块本身中构build表单数据:
- (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数据或查询string参数,只有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的github回购中发布了一个bug。
罗布是绝对正确的,你所看到的问题与(现在已经结束的) 1398问题有关 。 但是,我想提供一个快速tl;博士以防其他人在寻找。
首先, 下面是github上gberginc提供的代码片段,您可以在之后对file upload进行build模:
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]; }];
其次,为了总结这个问题(以及为什么你必须使用一个临时文件作为解决方法),这实际上是两方面的。
- 苹果公司认为内容长度头受其控制,当为
NSURLRequest
设置HTTP正文stream时,Apple的库将编码设置为分块,然后放弃该头(从而清除任何内容长度值AFNetworking集) - 上传的服务器不支持
Transfer-Encoding: Chunked
(例如S3)
但事实certificate,如果你正在上传文件请求(因为总的请求大小是提前知道的),Apple的库会正确设置内容长度标题。 疯了吧?
进一步深入研究,看起来,当你使用NSURLSession
和setHTTPBodyStream
结合使用setHTTPBodyStream
,即使请求设置了Content-Length
(在requestByFinalizingMultipartFormData
中AFURLRequestSerialization
),那么这个头文件也不会被发送。 您可以通过比较任务的originalRequest
和currentRequest
的所有allHTTPHeaderFields
来确认。 我也向查尔斯证实了这一点。
有趣的是, Transfer-Encoding
被设置为chunked
(当长度未知时,这通常是正确的)。
底线,这似乎是AFNetworkingselect使用setHTTPBodyStream
而不是setHTTPBody
(它不会遭受这种行为)的一种performanceforms,它与NSURLSession
结合导致此畸形请求的行为。
我认为这与AFNetworking 问题1398有关 。
我自己也遇到了这个问题,在这里尝试了两种方法和build议的方法。
事实certificate,这与将附加数据“名称”键更改为“文件”而不是文件名variables一样简单。
确保您的数据密钥匹配,否则您将看到与空数据集相同的症状。