2014-01-06 38 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

我跑了這個問題我自己,並試圖兩種方法,在這裏建議的方法...

事實證明,這是爲改變附加數據「姓名」鍵「文件」,而不是簡單的文件名變量。

確保您的數據密鑰匹配,否則您將看到空數據集的相同症狀。