使用 Objective-C POST 多部分/表单数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24250475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
POST multipart/form-data with Objective-C
提问by Ali Almohsen
So this HTML code submits the data in the correct format for me.
所以这个 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>
I've looked into a good number of articles on how to do a multipart/form-data POST on iOS, but none really explain what to do if there were normal parameters as well as the file upload.
我已经研究了很多关于如何在 iOS 上进行多部分/表单数据 POST 的文章,但没有人真正解释如果有正常参数以及文件上传时该怎么做。
Could you please help me with the code to POST this in Obj-C?
你能帮我用代码在 Obj-C 中发布这个吗?
Thanks!
谢谢!
回答by Rob
The process is as follows:
过程如下:
Create dictionary with the
userName,userEmail, anduserPasswordparameters.NSDictionary *params = @{@"userName" : @"rob", @"userEmail" : @"[email protected]", @"userPassword" : @"password"};Determine the path for the image:
NSString *path = [[NSBundle mainBundle] pathForResource:@"avatar" ofType:@"png"];Create the request:
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];This is the method used above to build the body of the request:
- (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; }The above uses the following utility methods:
@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]]; }Then submit the request. There are many, many options here.
For example, if using
NSURLSession, you could createNSURLSessionUploadTask: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];Or you could create a
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];The above assumes that the server is just returning text response. It's better if the server returned JSON, in which case you'd use
NSJSONSerializationrather thanNSStringmethodinitWithData.Likewise, I'm using the completion block renditions of
NSURLSessionabove, but feel free to use the richer delegate-based renditions, too. But that seems beyond the scope of this question, so I'll leave that to you.
创建字典的
userName,userEmail和userPassword参数。NSDictionary *params = @{@"userName" : @"rob", @"userEmail" : @"[email protected]", @"userPassword" : @"password"};确定图像的路径:
NSString *path = [[NSBundle mainBundle] pathForResource:@"avatar" ofType:@"png"];创建请求:
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];这是上面用于构建请求正文的方法:
- (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; }以上使用了以下实用方法:
@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]]; }然后提交请求。这里有很多很多选择。
例如,如果使用
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而不是NSStringmethodinitWithData。同样,我正在使用上面的完成块再现
NSURLSession,但也可以随意使用更丰富的基于委托的再现。但这似乎超出了这个问题的范围,所以我将把它留给你。
But hopefully this illustrates the idea.
但希望这说明了这个想法。
I'd be remiss if I didn't point that, much easier than the above, you can use AFNetworking, repeating steps 1 and 2 above, but then just calling:
如果我没有指出这一点,我会失职,比上面的要容易得多,您可以使用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:&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.");
}
回答by manish gaurav
POST multiple images using multipart or form-data with Objective-C
使用带有 Objective-C 的 multipart 或 form-data POST 多个图像
-(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);
}
回答by Arvind Patidar
Try to use this for both video and image data with different mime types.
尝试将其用于具有不同 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];
回答by Mutawe
I struggled with this for a while, if you are looking for uploading multiple images or any other file types, you can do the following using AFNetworking 3.0
我为此苦苦挣扎了一段时间,如果您正在寻找上传多个图像或任何其他文件类型,您可以使用以下方法执行以下操作 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.");
}
And here is how my AttachmentsModel looks like:
这是我的 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;

