public
Last active

Objective-C method for composing a HTTP multipart/form-data body

  • Download Gist
gistfile1.m
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
// Objective-C method for composing a HTTP multipart/form-data body.
// Provide parameters and data in a NSDictionary. Outputs a NSData request body.
// License: Public Domain
// Author: Leonard van Driel, 2012
 
@interface MultipartExample @end
 
@implementation MultipartExample
 
+ (void)exampleUsageUploadImage
{
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"<image>"]);
NSDictionary *parameters = @{@"<image-name.png>":imageData,@"<some-other-key>":@"<some-other-value>"};
NSString *url = @"https://myserver.com/upload";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[self addMultipartDataWithParameters:parameters toURLRequest:request];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:nil];
}
 
#pragma mark - Content-type: application/x-www-form-urlencoded
 
+ (void)addFormDataWithParameters:(NSDictionary *)parameters toURLRequest:(NSMutableURLRequest *)request
{
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];
NSString *query = [self joinQueryWithDictionary:parameters];
request.HTTPBody = [query dataUsingEncoding:NSUTF8StringEncoding];
}
 
 
#pragma mark - Content-type: multipart/form-data
 
+ (void)addMultipartDataWithParameters:(NSDictionary *)parameters toURLRequest:(NSMutableURLRequest *)request
{
NSString *boundary = nil;
NSData *post = [self multipartDataWithParameters:parameters boundary:&boundary];
[request setValue:[@"multipart/form-data; boundary=" stringByAppendingString:boundary] forHTTPHeaderField:@"Content-type"];
request.HTTPBody = post;
}
 
+ (NSData *)multipartDataWithParameters:(NSDictionary *)parameters boundary:(NSString **)boundary
{
NSMutableData *result = [[NSMutableData alloc] init];
if (boundary && !*boundary) {
char buffer[32];
for (NSUInteger i = 0; i < 32; i++) buffer[i] = "0123456789ABCDEF"[rand() % 16];
NSString *random = [[NSString alloc] initWithBytes:buffer length:32 encoding:NSASCIIStringEncoding];
*boundary = [NSString stringWithFormat:@"MyApp--%@", random];
}
NSData *newline = [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding];
NSData *boundaryData = [[NSString stringWithFormat:@"--%@\r\n", boundary ? *boundary : @""] dataUsingEncoding:NSUTF8StringEncoding];
for (NSArray *pair in [self flatten:parameters]) {
[result appendData:boundaryData];
[self appendToMultipartData:result key:pair[0] value:pair[1]];
[result appendData:newline];
}
NSString *end = [NSString stringWithFormat:@"--%@--\r\n", boundary ? *boundary : @""];
[result appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];
return result;
}
 
+ (void)appendToMultipartData:(NSMutableData *)data key:(NSString *)key value:(id)value
{
if ([value isKindOfClass:NSData.class]) {
NSString *name = key;
if ([key rangeOfString:@"%2F"].length) {
NSRange r = [name rangeOfString:@"%2F"];
key = [key substringFromIndex:r.location + r.length];
name = [name substringToIndex:r.location];
}
NSString *string = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\nContent-Type: application/octet-stream\r\n\r\n", name, key];
[data appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:value];
} else {
NSString *string = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@", key, value];
[data appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
}
}
 
 
#pragma mark - URL
 
+ (NSString *)urlWithBase:(NSString *)root path:(NSArray *)path query:(NSDictionary *)query
{
NSMutableString *result = [[NSMutableString alloc] initWithString:root];
if (path) {
if (![result hasSuffix:@"/"]) [result appendString:@"/"];
[result appendString:[self joinPathWithArray:path]];
}
if (query) {
if (![result rangeOfString:@"?"].length) [result appendString:@"?"];
else if (![result hasSuffix:@"?"] && ![result hasSuffix:@"&"]) [result appendString:@"&"];
[result appendString:[self joinQueryWithDictionary:query]];
}
return result;
}
 
+ (NSString *)joinPathWithArray:(NSArray *)array
{
NSMutableString *result = [[NSMutableString alloc] init];
for (id component in array) {
if (result.length) [result appendString:@"/"];
[result appendString:[self escape:[component description]]];
}
return result;
}
 
+ (NSString *)joinQueryWithDictionary:(NSDictionary *)dictionary
{
NSMutableString *result = [[NSMutableString alloc] init];
for (NSArray *pair in [self flatten:dictionary]) {
if (result.length) [result appendString:@"&"];
[result appendString:pair[0]];
[result appendString:@"="];
[result appendString:[self escape:[pair[1] description]]];
}
return result;
}
 
+ (NSDictionary *)splitQueryWithString:(NSString *)string {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *pair in [string componentsSeparatedByString:@"&"]) {
NSRange r = [pair rangeOfString:@"="];
if (r.location == NSNotFound) {
[result setObject:@"" forKey:[self unescape:pair]];
} else {
NSString *value = [self unescape:[pair substringFromIndex:r.location + r.length]];
[result setObject:value forKey:[self unescape:[pair substringToIndex:r.location]]];
}
}
return result;
}
 
 
#pragma mark - Helpers
 
+ (NSString *)unescape:(NSString *)string
{
return CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)string, CFSTR(""), kCFStringEncodingUTF8));
}
 
+ (NSString *)escape:(NSString *)string
{
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef)string, NULL, CFSTR("*'();:@&=+$,/?!%#[]"), kCFStringEncodingUTF8));
}
 
+ (NSArray *)flatten:(NSDictionary *)dictionary
{
NSMutableArray *result = [NSMutableArray arrayWithCapacity:dictionary.count];
NSArray *keys = [dictionary.allKeys sortedArrayUsingSelector:@selector(compare:)];
for (NSString *key in keys) {
id value = [dictionary objectForKey:key];
if ([value isKindOfClass:NSArray.class] || [value isKindOfClass:NSSet.class]) {
NSString *k = [[self escape:key] stringByAppendingString:@"[]"];
for (id v in value) {
[result addObject:@[k, v]];
}
} else if ([value isKindOfClass:NSDictionary.class]) {
for (NSString *k in value) {
NSString *kk = [[self escape:key] stringByAppendingFormat:@"[%@]", [self escape:k]];
[result addObject:@[kk, [value valueForKey:k]]];
}
} else {
[result addObject:@[[self escape:key], value]];
}
}
return result;
}
 
@end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.