POST multipart / form-data com Objective-C

Então, esse código HTML envia os dados no formato correto para mim.

 Name: 
Email:
Password:
Avatar:

Eu olhei em um bom número de artigos sobre como fazer um POST multipart / formulário de dados no iOS, mas nenhum realmente explica o que fazer se houvesse parâmetros normais, bem como o upload do arquivo.

Você poderia me ajudar com o código para postar isso em Obj-C?

Obrigado!

O processo é como se segue:

  1. Crie um dictionary com os parâmetros userName , userEmail e userPassword .

     NSDictionary *params = @{@"userName" : @"rob", @"userEmail" : @"rob@email.com", @"userPassword" : @"password"}; 
  2. Determine o caminho da imagem:

     NSString *path = [[NSBundle mainBundle] pathForResource:@"avatar" ofType:@"png"]; 
  3. Crie o pedido:

     NSString *boundary = [self generateBoundaryString]; // configure the request NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; [request setHTTPMethod:@"POST"]; // set content type NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; [request setValue:contentType forHTTPHeaderField: @"Content-Type"]; // create body NSData *httpBody = [self createBodyWithBoundary:boundary parameters:params paths:@[path] fieldName:fieldName]; 
  4. Este é o método usado acima para construir o corpo da solicitação:

     - (NSData *)createBodyWithBoundary:(NSString *)boundary parameters:(NSDictionary *)parameters paths:(NSArray *)paths fieldName:(NSString *)fieldName { NSMutableData *httpBody = [NSMutableData data]; // add params (all params are strings) [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) { [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", parameterKey] dataUsingEncoding:NSUTF8StringEncoding]]; [httpBody appendData:[[NSString stringWithFormat:@"%@\r\n", parameterValue] dataUsingEncoding:NSUTF8StringEncoding]]; }]; // add image data for (NSString *path in paths) { NSString *filename = [path lastPathComponent]; NSData *data = [NSData dataWithContentsOfFile:path]; NSString *mimetype = [self mimeTypeForPath:path]; [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, filename] dataUsingEncoding:NSUTF8StringEncoding]]; [httpBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype] dataUsingEncoding:NSUTF8StringEncoding]]; [httpBody appendData:data]; [httpBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; } [httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; return httpBody; } 
  5. O acima usa os seguintes methods de utilitário:

     @import MobileCoreServices; // only needed in iOS - (NSString *)mimeTypeForPath:(NSString *)path { // get a mime type for an extension using MobileCoreServices.framework CFStringRef extension = (__bridge CFStringRef)[path pathExtension]; CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL); assert(UTI != NULL); NSString *mimetype = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType)); assert(mimetype != NULL); CFRelease(UTI); return mimetype; } - (NSString *)generateBoundaryString { return [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]]; } 
  6. Em seguida, envie a solicitação. Existem muitas, muitas opções aqui.

    Por exemplo, se estiver usando NSURLSession , você poderá criar NSURLSessionUploadTask :

     NSURLSession *session = [NSURLSession sharedSession]; // use sharedSession or create your own NSURLSessionTask *task = [session uploadTaskWithRequest:request fromData:httpBody completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error = %@", error); return; } NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"result = %@", result); }]; [task resume]; 

    Ou você poderia criar um NSURLSessionDataTask :

     request.HTTPBody = httpBody; NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error = %@", error); return; } NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"result = %@", result); }]; [task resume]; 

    O acima assume que o servidor está apenas retornando a resposta de texto. É melhor se o servidor retornasse JSON, nesse caso você usaria NSJSONSerialization vez do método NSString initWithData .

    Da mesma forma, estou usando as renderizações de bloco de conclusão de NSURLSession acima, mas sinta-se à vontade para usar também as renderizações baseadas em delegado mais ricas. Mas isso parece além do escopo desta questão, então deixarei isso para você.

Mas espero que isso ilustra a ideia.


Eu seria negligente se eu não apontasse isso, muito mais fácil do que o acima, você pode usar o AFNetworking , repetindo os passos 1 e 2 acima, mas apenas ligando:

 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // only needed if the server is not returning JSON; if web service returns JSON, remove this line NSURLSessionTask *task = [manager POST:urlString parameters:params constructingBodyWithBlock:^(id formData) { NSError *error; if (![formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:@"avatar" fileName:[path lastPathComponent] mimeType:@"image/png" error:&error]) { NSLog(@"error appending part: %@", error); } } progress:nil success:^(NSURLSessionTask *task, id responseObject) { NSLog(@"responseObject = %@", responseObject); } failure:^(NSURLSessionTask *task, NSError *error) { NSLog(@"error = %@", error); }]; if (!task) { NSLog(@"Creation of task failed."); } 

Tente usar isso para dados de vídeo e imagens com diferentes tipos de mímica.

 NSDictionary *param; AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; // 1. Create `AFHTTPRequestSerializer` which will create your request. AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer]; NSMutableURLRequest *request; NSData *fileData; if ([objDoc.url containsString:@".mp4"]) { manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:@"application/json"]; [serializer setValue:@"video/mp4" forHTTPHeaderField:@"Content-Type"]; manager.requestSerializer = serializer; } // 2. Create an `NSMutableURLRequest`. NSLog(@"filename =%@",objDoc.url); request= [serializer multipartFormRequestWithMethod:@"POST" URLString:strUrl parameters:param constructingBodyWithBlock:^(id formData) { if ([objDoc.url containsString:@".mp4"]) { [formData appendPartWithFileData:fileData name:@"File" fileName:@"video.mp4" mimeType:@"video/mp4"]; }else{ [formData appendPartWithFileData:fileData name:@"File" fileName:@"image.jpeg" mimeType:@"image/jpeg"]; } } error:nil]; // 3. Create and use `AFHTTPRequestOperationManager` to create an `AFHTTPRequestOperation` from the `NSMutableURLRequest` that we just created. self.objeDocument.isUploading = [NSNumber numberWithInt:1]; self.operation = [manager HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"Success %@", responseObject); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Error!" message:@"The document attached has failed to upload." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [alert show]; [self.operation cancel]; NSLog(@"Failure %@", error.description); }]; // 4. Set the progress block of the operation. [self.operation setUploadProgressBlock:^(NSUInteger __unused bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { NSLog(@"Wrote %lld/%lld", totalBytesWritten, totalBytesExpectedToWrite); float progress = (float)totalBytesWritten/(float)totalBytesExpectedToWrite; }]; // 5. Begin! [self.operation start]; 

POST várias imagens usando multipartes ou dados de formulário com Objective-C

 -(void)multipleimageandstring { NSString *urlString=@"URL NAME"; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init] ; [request setURL:[NSURL URLWithString:urlString]]; [request setHTTPMethod:@"POST"]; NSMutableData *body = [NSMutableData data]; NSString *boundary = @"---------------------------14737809831466499882746641449"; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; [request addValue:contentType forHTTPHeaderField:@"Content-Type"]; // file float low_bound = 0; float high_bound =5000; float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);//image1 int intRndValue = (int)(rndValue + 0.5); NSString *str_image1 = [@(intRndValue) stringValue]; UIImage *chosenImage1=[UIImage imageNamed:@"Purchase_GUI_curves-12 copy.png"]; NSData *imageData = UIImageJPEGRepresentation(chosenImage1, 90); [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"files\"; filename=\"%@.png\"\r\n",str_image1] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[NSData dataWithData:imageData]]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"name\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"Nilesh" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"apipassword\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithString:app.password] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"adminId\"\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[[NSString stringWithString:app.adminId] dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; // close form [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; // set request body [request setHTTPBody:body]; //return and test NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding]; NSLog(@"%@", returnString); } 

Eu lutei com isso por um tempo, se você estiver procurando por upload de várias imagens ou qualquer outro tipo de arquivo, você pode fazer o seguinte usando o AFNetworking 3.0

 NSDictionary *params = @{key : value, ..... etc }; NSString *urlString = @"http://..... your endpoint url"; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; // only needed if the server is not returning JSON; NSURLSessionTask *task = [manager POST:urlString parameters:params constructingBodyWithBlock:^(id formData) { for (int x = 0 ; x< contentArray.count; x++) { AttachmentsModel *model = contentArray[x]; if(model.type == ImageAttachmentType){ [formData appendPartWithFileData:model.data name:model.name fileName:model.fileName mimeType:model.mimeType]; }else if(model.type == AudioAttachmentType){ NSURL *urlVideoFile = [NSURL fileURLWithPath:model.path]; [formData appendPartWithFileURL:urlVideoFile name:model.name fileName:model.fileName mimeType:model.mimeType error:nil]; }else{ [formData appendPartWithFileURL:model.url name:model.name fileName:model.fileName mimeType:model.mimeType error:nil]; } } } progress:nil success:^(NSURLSessionTask *task, id responseObject) { [Utility stopLoading]; NSString *result = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; NSLog(@"result = %@", result); id json = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil]; if (block) { //your response comes here } } failure:^(NSURLSessionTask *task, NSError *error) { NSLog(@"error = %@", error); }]; if (!task) { NSLog(@"Creation of task failed."); } 

E aqui está como meu AttachmentsModel se parece:

 // AttachmentsModel.h typedef enum AttachmnetType{ ImageAttachmentType, AudioAttachmentType, VideoAttachmentType } AttachmnetType; @interface AttachmentsModel : NSObject @property (strong, nonatomic) NSString *path; @property (strong, nonatomic) NSData *data; @property (strong, nonatomic) NSString *mimeType; @property (strong, nonatomic) NSString *name; @property (strong, nonatomic) NSString *fileName; @property (strong, nonatomic) NSURL *url;