From b621ad08481015a64500ca979e2155cc55249a8a Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Fri, 23 Sep 2011 10:21:07 -0500 Subject: [PATCH] Changing multipart form data object to be subclass of NSMutableData Adding mimetype arguments to some multipart methods --- AFNetworking/AFHTTPClient.h | 16 +++++---- AFNetworking/AFHTTPClient.m | 70 ++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/AFNetworking/AFHTTPClient.h b/AFNetworking/AFHTTPClient.h index 3c1fc3d..b3c3b60 100644 --- a/AFNetworking/AFHTTPClient.h +++ b/AFNetworking/AFHTTPClient.h @@ -250,20 +250,22 @@ - (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body; /** - Appends the HTTP header `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary. + Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"` and, if mimeType is specified, `Content-Type: #{mimeType}`, followed by the encoded data and the multipart form boundary. @param data The data to be encoded and appended to the form data. - @param name The name to be associated with the specified data. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. If `nil`, the `Content-Type` header will be omitted. + @param name The name to be associated with the specified data. This parameter must not be `nil`. */ -- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name; +- (void)appendPartWithFormData:(NSData *)data mimeType:(NSString *)mimeType name:(NSString *)name; /** - Appends the HTTP header `Content-Disposition: file; filename=#{filename}"`, followed by the encoded file data and the multipart form boundary. + Appends the HTTP header `Content-Disposition: file; filename=#{filename}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary. - @param fileURL The URL for the local file to have its contents appended to the form data. - @param fileNameOrNil The filename to be associated with the file contents. If `nil`, the last path component followed by its file extension will be used instead. + @param fileURL The URL for the local file to have its contents appended to the form data. This parameter must not be `nil`. + @param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`. + @param fileName The filename to be associated with the file contents. This parameter must not be `nil`. */ -- (void)appendPartWithFile:(NSURL *)fileURL fileName:(NSString *)fileNameOrNil; +- (void)appendPartWithFile:(NSURL *)fileURL mimeType:(NSString *)mimeType fileName:(NSString *)fileName; /** Appends encoded data to the form data. diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 9ba7613..52818ea 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -34,16 +34,14 @@ static NSString * AFMultipartFormFinalBoundary() { return [NSString stringWithFormat:@"--%@--", kAFMultipartFormBoundary]; } -@interface AFMutableMultipartFormData : NSObject { +@interface AFMutableMultipartFormData : NSMutableData { @private NSStringEncoding _stringEncoding; - NSMutableArray *_mutableLines; + NSRange _finalBoundaryRange; } - (id)initWithStringEncoding:(NSStringEncoding)encoding; -- (NSData *)data; - @end #pragma mark - @@ -207,19 +205,23 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS NSEnumerator *enumerator = [parameters keyEnumerator]; while ((key = [enumerator nextObject])) { id value = [parameters valueForKey:key]; - if (![value isKindOfClass:[NSData class]]) { - value = [value description]; + NSData *data = nil; + + if ([value isKindOfClass:[NSData class]]) { + data = value; + } else { + data = [[value description] dataUsingEncoding:self.stringEncoding]; } - [formData appendPartWithFormData:[value dataUsingEncoding:self.stringEncoding] name:[key description]]; - } + [formData appendPartWithHeaders:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"form-data; name=\"%@\"", [key description]] forKey:@"Content-Disposition"] body:value]; + } if (block) { block(formData); } [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"]; - [request setHTTPBody:[formData data]]; + [request setHTTPBody:formData]; [formData autorelease]; @@ -272,14 +274,14 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS // multipart/form-data; see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2 @interface AFMutableMultipartFormData () @property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding; -@property (readwrite, nonatomic, retain) NSMutableArray *mutableLines; +@property (readwrite, nonatomic, assign) NSRange finalBoundaryRange; - (void)appendBlankLine; @end @implementation AFMutableMultipartFormData @synthesize stringEncoding = _stringEncoding; -@synthesize mutableLines = _mutableLines; +@synthesize finalBoundaryRange = _finalBoundaryRange; - (id)initWithStringEncoding:(NSStringEncoding)encoding { self = [super init]; @@ -288,28 +290,15 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS } self.stringEncoding = encoding; - self.mutableLines = [NSMutableArray array]; + self.finalBoundaryRange = NSMakeRange(0, 0); return self; } -- (void)dealloc { - [_mutableLines release]; - [super dealloc]; -} - -- (NSData *)data { - if ([self.mutableLines count] == 0) { - return nil; - } - - return [[[[self.mutableLines componentsJoinedByString:kAFMultipartFormLineDelimiter] stringByAppendingString:kAFMultipartFormLineDelimiter] stringByAppendingString:AFMultipartFormFinalBoundary()] dataUsingEncoding:self.stringEncoding]; -} - #pragma mark - AFMultipartFormDataProxy - (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body { - if ([self.mutableLines count] > 0) { + if ([self length] > 0) { [self appendString:AFMultipartFormEncapsulationBoundary()]; } @@ -321,27 +310,42 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS [self appendData:body]; } -- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name { - [self appendPartWithHeaders:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"] body:data]; +- (void)appendPartWithFormData:(NSData *)data mimeType:(NSString *)mimeType name:(NSString *)name { + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"]; + if (mimeType) { + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + } + + [self appendPartWithHeaders:mutableHeaders body:data]; } -- (void)appendPartWithFile:(NSURL *)fileURL fileName:(NSString *)fileNameOrNil { +- (void)appendPartWithFile:(NSURL *)fileURL mimeType:(NSString *)mimeType fileName:(NSString *)fileName { if (![fileURL isFileURL]) { [NSException raise:@"Invalid fileURL value" format:@"%@ must be a valid file URL", fileURL]; return; } + NSMutableDictionary *mutableHeaders = [NSMutableDictionary dictionary]; + [mutableHeaders setValue:[NSString stringWithFormat:@"file; filename=\"%@\"", fileName] forKey:@"Content-Disposition"]; + [mutableHeaders setValue:mimeType forKey:@"Content-Type"]; + NSData *data = [NSData dataWithContentsOfFile:[fileURL absoluteString]]; - NSString *fileName = fileNameOrNil ? fileNameOrNil : [[fileURL lastPathComponent] stringByAppendingPathExtension:[fileURL pathExtension]]; - [self appendPartWithHeaders:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"file; filename=\"%@\"", fileName] forKey:@"Content-Disposition"] body:data]; + + [self appendPartWithHeaders:mutableHeaders body:data]; } - (void)appendData:(NSData *)data { - [self appendString:[[[NSString alloc] initWithData:data encoding:self.stringEncoding] autorelease]]; + NSMutableData *mutableData = [NSMutableData dataWithData:data]; + [self replaceBytesInRange:self.finalBoundaryRange withBytes:[mutableData bytes]]; + + NSData *finalBoundary = [AFMultipartFormFinalBoundary() dataUsingEncoding:self.stringEncoding]; + self.finalBoundaryRange = NSMakeRange([self length], [finalBoundary length]); + [super appendData:finalBoundary]; } - (void)appendString:(NSString *)string { - [self.mutableLines addObject:string]; + [self appendData:[string dataUsingEncoding:self.stringEncoding]]; } - (void)appendBlankLine {