Using an iPhone to post binary files like photos or video to a remote server is not a difficult task, but there’s a wide margin for inexplicable sinkholes ready to swallow precious time with formatting nuances or missing headers. I say this while kicking the muck off my boots myself, as trial by error and a measure of patchwork has brought me to following happy implementation.
- Create an NSMutableURLRequest
- Load file as NSData
- Construct a multipart/form-data formatted post body
- Set the request headers
- Assign an NSURLConnection delegate
Step 3 is the heart of the matter. Assembling a UTF8 string of post fields, multipart form boundaries, and a binary dump of the file data is an endeavor where every whitespace and newline could potentially ruin your day. The project’s app delegate is assigned as the NSURLConnection delegate (although it could be any object) in order to receive callbacks from the request and handle request or failure conditions.
The following example’s UIViewController responds to an IBAction button click by creating an HTTP Post request whose body contains a photo and couple of associated form fields. The Content-Type content header is set to image/jpeg for the photo upload and the Content-Length header is set to the length of the final body string. (note, in the following code I’ve omitted the headers for clarity)
MultipartUploadViewController.m
#import "MultipartUploadViewController.h"
#import "MultipartUploadAppDelegate.h"
@implementation MultipartUploadViewController
- (IBAction)postForm:(id)sender {
MultipartUploadAppDelegate *appDelegate = (MultipartUploadAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *urlString = :@"http://www.example.com/services";
NSURL *url = [NSURL URLWithString:urlString];
NSString *boundary = @"----1010101010";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];
NSString *photoPath = [[NSBundle mainBundle] pathForResource:@" my-photo " ofType:@"jpg"];
NSData *photoData = [NSData dataWithContentsOfFile:photoPath];
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Disposition: form-data; name=\"photo-description\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"testing 123" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"photo-file\"; filename=\"%@\"\r\n", " my-photo.jpg"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:photoData];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Disposition: form-data; name=\"tags\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"random,test,example" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
[request addValue:[NSString stringWithFormat:@"%d", body.length] forHTTPHeaderField: @"Content-Length"];
[[NSURLConnection alloc] initWithRequest:request delegate:appDelegate];
[photoData release];
[body release];
[request release];
}
@end
The App Delegate below simply contains a few logging calls to track the status of the file upload in the NSURLConnectionDelegate response methods.
MultipartUploadAppDelegate.m
#import "MultipartUploadAppDelegate.h"
#import "MultipartUploadViewController.h"
@implementation MultipartUploadAppDelegate
@synthesize window;
@synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@"Response Recieved");
NSLog(@"Response Code: %d",[response statusCode]);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(@"Data Recieved");
NSString *content = [NSString stringWithUTF8String:[data bytes]];
NSLog(@"DATA: %@",content);
}
@end




