AFNetworking 2.0 corps de requête multipart vierge

Similaire à ce problème .

En utilisant AFNetworking 2.0.3 et en essayant de download une image en utilisant POST + de AFHTTPSessionManager constructingBodyWithBlock. Pour des raisons inconnues, il semble que le corps du message HTTP soit toujours vide lorsque la requête est faite au server.

Je sous-class AFHTTPSessionManager ci-dessous (d'où l'utilisation de [self POST ...] .

J'ai essayé de build la request de deux façons.

Méthode 1 : J'ai juste essayé de passer des parameters et d'append seulement datatables d'image si elles existent.

 - (void) createNewAccount:(NSSsortingng *)nickname accountType:(NSInteger)accountType primaryPhoto:(UIImage *)primaryPhoto { NSSsortingng *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); }]; } 

Méthode 2 : essayé de générer datatables de formulaire dans le bloc lui-même:

 - (void) createNewAccount:(NSSsortingng *)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:NSUTF8SsortingngEncoding] name:@"nickname"]; [formData appendPartWithFormData:[NSData dataWithBytes:&accountType length:sizeof(accountType)] name:@"type"]; if (self.accessToken) [formData appendPartWithFormData:[self.accessToken dataUsingEncoding:NSUTF8SsortingngEncoding] 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); }]; } 

En utilisant l'une ou l'autre méthode, lorsque la requête HTTP touche le server, il n'y a pas de données POST ou de parameters de string de requête, seulement des en-têtes 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 

Des pensées? Également posté un bug dans le repo github d'AFNetworking .

Rob a absolument raison, le problème que vous voyez est lié au problème (maintenant fermé) 1398 . Cependant, je voulais fournir un petit message rapide au cas où quelqu'un d'autre regardait.

Tout d'abord, voici un extrait de code fourni par gberginc sur github que vous pouvez modéliser vos téléchargements de files après:

 NSSsortingng* 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. NSSsortingng* tmpFilename = [NSSsortingng ssortingngWithFormat:@"%f", [NSDate timeIntervalSinceReferenceDate]]; NSURL* tmpFileUrl = [NSURL fileURLWithPath:[NSTemporaryDirectory() ssortingngByAppendingPathComponent:tmpFilename]]; // Create a multipart form request. NSMutableURLRequest *multipartRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLSsortingng: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]; }]; 

Et deuxièmement, pour résumer le problème (et pourquoi vous devez utiliser un file temporaire pour contourner le problème), c'est vraiment double.

  1. Apple considère que l'en-tête content-length est sous son contrôle et lorsqu'un stream de corps HTTP est défini pour une NSURLRequest les bibliothèques d'Apple verront l'enencoding à Chunked puis abandonneront cet en-tête (effaçant ainsi toute valeur de longueur de contenu AFNetworking sets)
  2. Le server sur lequel le téléchargement est en cours ne prend pas en charge le Transfer-Encoding: Chunked (par exemple, S3)

Mais il s'avère que si vous téléchargez une requête à partir d'un file (car la taille totale de la requête est connue à l'avance), les bibliothèques d'Apple définiront correctement l'en-tête de longueur du contenu. Fou non?

En creusant plus loin, il apparaît que lorsque vous utilisez NSURLSession conjointement avec setHTTPBodyStream , même si la requête définit Content-Length (que fait AFURLRequestSerialization dans requestByFinalizingMultipartFormData ), cet en-tête n'est pas envoyé. Vous pouvez le confirmer en comparant les allHTTPHeaderFields de la tâche originalRequest et currentRequest de la tâche. J'ai aussi confirmé cela avec Charles.

Ce qui est intéressant, c'est que Transfer-Encoding est défini comme chunked (ce qui est correct en général quand la longueur est inconnue).

Bottom line, cela semble être une manifestation du choix d' setHTTPBodyStream utiliser setHTTPBodyStream plutôt que setHTTPBody (qui ne souffre pas de ce comportement), ce qui, lorsqu'il est combiné avec NSURLSession traduit par ce comportement de requêtes mal formées.

Je pense que cela est lié à AFNetworking numéro 1398 .

Je courais dans ce problème moi-même, et essayait les deux methods et la méthode suggérée ici …

Il s'avère que c'était aussi simple que de changer la key «nom» des données ajoutées à «file» au lieu de la variable du nom de file.

Assurez-vous que votre key de données correspond, sinon vous verrez le même symptôme d'un set de données vide.