ОПУБЛИКОВАТЬ составные / данные формы с помощью Objective-C

#objective-c #http-post #multipartform-data #http-post-vars

#objective-c #http-post #составная форма-данные #http-post-vars

Вопрос:

Итак, этот HTML-код отправляет данные в правильном для меня формате.

 <form action="https://www.example.com/register.php" method="post" enctype="multipart/form-data">
    Name: <input type="text" name="userName"><BR />
    Email: <input type="text" name="userEmail"><BR />
    Password: <input type="text" name="userPassword"><BR />
    Avatar: <input type="file" name="avatar"><BR />
    <input type="submit">
</form>
  

Я просмотрел большое количество статей о том, как сделать публикацию составных / данных формы на iOS, но ни одна из них не объясняет, что делать, если были обычные параметры, а также загрузка файла.

Не могли бы вы помочь мне с кодом для ПУБЛИКАЦИИ этого в Obj-C?

Спасибо!

Ответ №1:

Процесс выглядит следующим образом:

  1. Создайте словарь с параметрами userName , userEmail , и userPassword .

     NSDictionary *params = @{@"userName"     : @"rob",
                             @"userEmail"    : @"rob@email.com",
                             @"userPassword" : @"password"};
      
  2. Определите путь для изображения:

     NSString *path = [[NSBundle mainBundle] pathForResource:@"avatar" ofType:@"png"];
      
  3. Создайте запрос:

     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. Это метод, используемый выше для построения тела запроса:

     - (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:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
            [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"rnrn", parameterKey] dataUsingEncoding:NSUTF8StringEncoding]];
            [httpBody appendData:[[NSString stringWithFormat:@"%@rn", 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:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
            [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="%@"; filename="%@"rn", fieldName, filename] dataUsingEncoding:NSUTF8StringEncoding]];
            [httpBody appendData:[[NSString stringWithFormat:@"Content-Type: %@rnrn", mimetype] dataUsingEncoding:NSUTF8StringEncoding]];
            [httpBody appendData:data];
            [httpBody appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
        }
    
        [httpBody appendData:[[NSString stringWithFormat:@"--%@--rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    
        return httpBody;
    }
      
  5. В приведенном выше примере используются следующие служебные методы:

     @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. Затем отправьте запрос. Здесь есть много, много вариантов.

    Например, при использовании NSURLSession вы могли бы создать 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];
      

    Или вы могли бы создать 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];
      

    Вышеизложенное предполагает, что сервер просто возвращает текстовый ответ. Лучше, если сервер вернул JSON, и в этом случае вы бы использовали NSJSONSerialization метод, а не NSString метод initWithData .

    Аналогично, я использую NSURLSession приведенные выше версии блоков завершения, но не стесняйтесь использовать и более богатые версии на основе делегатов. Но это, кажется, выходит за рамки этого вопроса, поэтому я оставлю это вам.

Но, надеюсь, это иллюстрирует идею.


Я был бы упущением, если бы не указал, что гораздо проще, чем указано выше, вы можете использовать AFNetworking, повторив шаги 1 и 2 выше, но затем просто позвонив:

 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<AFMultipartFormData> formData) {
    NSError *error;
    if (![formData appendPartWithFileURL:[NSURL fileURLWithPath:path] name:@"avatar" fileName:[path lastPathComponent] mimeType:@"image/png" error:amp;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.");
}
  

Комментарии:

1. Обратите внимание, что вы также можете сгенерировать UUID с помощью NSUUID: NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];

2. Да, если вам не нужно поддерживать версии iOS до версии 6.0, это очень хороший способ сделать это.

3. привет, ваш ответ очень полезен, но я просто хотел спросить, что такое fieldName NSString , потому что я не вижу, чтобы вы создавали его где-либо в своем коде, и я не уверен, что именно я должен там использовать

4. Это fieldName любое имя поля, которое ищет ваш серверный код при идентификации загрузок. Например, если ваш серверный код был написан на PHP, это имя любого поля, которое серверный код использует в своих $_FILES ссылках. В примере, включенном в исходный вопрос, это имя поля было avatar .

5. Очень подробный ответ.

Ответ №2:

ПУБЛИКАЦИЯ нескольких изображений с использованием составных или форм-данных с помощью 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:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="files"; filename="%@.png"rn",str_image1] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type: application/octet-streamrnrn" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[NSData dataWithData:imageData]];
    [body appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];





    [body appendData:[[NSString stringWithFormat:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="name"rnrn"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Nilesh" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];

    [body appendData:[[NSString stringWithFormat:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="apipassword"rnrn"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithString:app.password] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];

    [body appendData:[[NSString stringWithFormat:@"--%@rn", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name="adminId"rnrn"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithString:app.adminId] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];

    // close form
    [body appendData:[[NSString stringWithFormat:@"--%@--rn", 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);

}
  

Ответ №3:

Попробуйте использовать это как для видео, так и для графических данных с разными типами mime.

 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<AFMultipartFormData> 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];
  

Ответ №4:

Я некоторое время боролся с этим, если вы ищете загрузку нескольких изображений или любых других типов файлов, вы можете сделать следующее, используя 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<AFMultipartFormData> 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.");
}
  

И вот как выглядит моя AttachmentsModel:

 //  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;