问题
So this HTML code submits the data in the correct format for me.
<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.
Could you please help me with the code to POST this in Obj-C?
Thanks!
回答1:
The process is as follows:
Create dictionary with the
userName
,userEmail
, anduserPassword
parameters.NSDictionary *params = @{@"userName" : @"rob", @"userEmail" : @"rob@email.com", @"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
NSJSONSerialization
rather thanNSString
methodinitWithData
.Likewise, I'm using the completion block renditions of
NSURLSession
above, 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.
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:
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.");
}
回答2:
POST multiple images using multipart or form-data with 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);
}
回答3:
Try to use this for both video and image data with different mime types.
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:
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
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.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;
来源:https://stackoverflow.com/questions/24250475/post-multipart-form-data-with-objective-c