//

// ASIFormDataRequest.m

// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest

//

// Created by Ben Copsey on 07/11/2008.

// Copyright 2008-2009 All-Seeing Interactive. All rights reserved.

//

#import "ASIFormDataRequest.h"

// Private stuff

@interface ASIFormDataRequest ()

- (void)buildMultipartFormDataPostBody;

- (void)buildURLEncodedPostBody;

- (void)appendPostString:(NSString *)string;

@property (retain) NSMutableArray *postData;

@property (retain) NSMutableArray *fileData;

#if DEBUG_FORM_DATA_REQUEST

- (void)addToDebugBody:(NSString *)string;

@property (retain, nonatomic) NSString *debugBodyString;

#endif

@end

@implementation ASIFormDataRequest

#pragma mark utilities

- (NSString*)encodeURL:(NSString *)string

{

NSString *newString = [NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding([self stringEncoding]))) autorelease];

if (newString) {

return newString;

}

return @"";

}

#pragma mark init / dealloc

+ (id)requestWithURL:(NSURL *)newURL

{

return [[[self alloc] initWithURL:newURL] autorelease];

}

- (id)initWithURL:(NSURL *)newURL

{

self = [super initWithURL:newURL];

[self setPostFormat:ASIURLEncodedPostFormat];

[self setStringEncoding:NSUTF8StringEncoding];

[self setRequestMethod:@"POST"];

return self;

}

- (void)dealloc

{

#if DEBUG_FORM_DATA_REQUEST

[debugBodyString release];

#endif

[postData release];

[fileData release];

[super dealloc];

}

#pragma mark setup request

- (void)addPostValue:(id )value forKey:(NSString *)key

{

if (!key) {

return;

}

if (![self postData]) {

[self setPostData:[NSMutableArray array]];

}

NSMutableDictionary *keyValuePair = [NSMutableDictionary dictionaryWithCapacity:2];

[keyValuePair setValue:key forKey:@"key"];

[keyValuePair setValue:[value description] forKey:@"value"];

[[self postData] addObject:keyValuePair];

}

- (void)setPostValue:(id )value forKey:(NSString *)key

{

// Remove any existing value

NSUInteger i;

for (i=0; i<[[self postData] count]; i++) {

NSDictionary *val = [[self postData] objectAtIndex:i];

if ([[val objectForKey:@"key"] isEqualToString:key]) {

[[self postData] removeObjectAtIndex:i];

i--;

}

}

[self addPostValue:value forKey:key];

}

- (void)addFile:(NSString *)filePath forKey:(NSString *)key

{

[self addFile:filePath withFileName:nil andContentType:nil forKey:key];

}

- (void)addFile:(NSString *)filePath withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key

{

BOOL isDirectory = NO;

BOOL fileExists = [[[[NSFileManager alloc] init] autorelease] fileExistsAtPath:filePath isDirectory:&isDirectory];

if (!fileExists || isDirectory) {

[self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"No file exists at %@",filePath],NSLocalizedDescriptionKey,nil]]];

}

// If the caller didn't specify a custom file name, we'll use the file name of the file we were passed

if (!fileName) {

fileName = [filePath lastPathComponent];

}

// If we were given the path to a file, and the user didn't specify a mime type, we can detect it from the file extension

if (!contentType) {

contentType = [ASIHTTPRequest mimeTypeForFileAtPath:filePath];

}

[self addData:filePath withFileName:fileName andContentType:contentType forKey:key];

}

- (void)setFile:(NSString *)filePath forKey:(NSString *)key

{

[self setFile:filePath withFileName:nil andContentType:nil forKey:key];

}

- (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key

{

// Remove any existing value

NSUInteger i;

for (i=0; i<[[self fileData] count]; i++) {

NSDictionary *val = [[self fileData] objectAtIndex:i];

if ([[val objectForKey:@"key"] isEqualToString:key]) {

[[self fileData] removeObjectAtIndex:i];

i--;

}

}

[self addFile:data withFileName:fileName andContentType:contentType forKey:key];

}

- (void)addData:(NSData *)data forKey:(NSString *)key

{

[self addData:data withFileName:@"file" andContentType:nil forKey:key];

}

- (void)addData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key

{

if (![self fileData]) {

[self setFileData:[NSMutableArray array]];

}

if (!contentType) {

contentType = @"application/octet-stream";

}

NSMutableDictionary *fileInfo = [NSMutableDictionary dictionaryWithCapacity:4];

[fileInfo setValue:key forKey:@"key"];

[fileInfo setValue:fileName forKey:@"fileName"];

[fileInfo setValue:contentType forKey:@"contentType"];

[fileInfo setValue:data forKey:@"data"];

[[self fileData] addObject:fileInfo];

}

- (void)setData:(NSData *)data forKey:(NSString *)key

{

[self setData:data withFileName:@"file" andContentType:nil forKey:key];

}

- (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key

{

// Remove any existing value

NSUInteger i;

for (i=0; i<[[self fileData] count]; i++) {

NSDictionary *val = [[self fileData] objectAtIndex:i];

if ([[val objectForKey:@"key"] isEqualToString:key]) {

[[self fileData] removeObjectAtIndex:i];

i--;

}

}

[self addData:data withFileName:fileName andContentType:contentType forKey:key];

}

- (void)buildPostBody

{

if ([self haveBuiltPostBody]) {

return;

}

#if DEBUG_FORM_DATA_REQUEST

[self setDebugBodyString:@""];

#endif

if (![self postData] && ![self fileData]) {

[super buildPostBody];

return;

}

if ([[self fileData] count] > 0) {

[self setShouldStreamPostDataFromDisk:YES];

}

if ([self postFormat] == ASIURLEncodedPostFormat) {

[self buildURLEncodedPostBody];

} else {

[self buildMultipartFormDataPostBody];

}

[super buildPostBody];

#if DEBUG_FORM_DATA_REQUEST

ASI_DEBUG_LOG(@"%@",[self debugBodyString]);

[self setDebugBodyString:nil];

#endif

}

- (void)buildMultipartFormDataPostBody

{

#if DEBUG_FORM_DATA_REQUEST

[self addToDebugBody:@"\r\n==== Building a multipart/form-data body ====\r\n"];

#endif

NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding]));

// We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does.

CFUUIDRef uuid = CFUUIDCreate(nil);

NSString *uuidString = [(NSString*)CFUUIDCreateString(nil, uuid) autorelease];

CFRelease(uuid);

NSString *stringBoundary = [NSString stringWithFormat:@"0xKhTmLbOuNdArY-%@",uuidString];

[self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"multipart/form-data; charset=%@; boundary=%@", charset, stringBoundary]];

[self appendPostString:[NSString stringWithFormat:@"--%@\r\n",stringBoundary]];

// Adds post data

NSString *endItemBoundary = [NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary];

NSUInteger i=0;

for (NSDictionary *val in [self postData]) {

[self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",[val objectForKey:@"key"]]];

[self appendPostString:[val objectForKey:@"value"]];

i++;

if (i != [[self postData] count] || [[self fileData] count] > 0) { //Only add the boundary if this is not the last item in the post body

[self appendPostString:endItemBoundary];

}

}

// Adds files to upload

i=0;

for (NSDictionary *val in [self fileData]) {

[self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [val objectForKey:@"key"], [val objectForKey:@"fileName"]]];

[self appendPostString:[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", [val objectForKey:@"contentType"]]];

id data = [val objectForKey:@"data"];

if ([data isKindOfClass:[NSString class]]) {

[self appendPostDataFromFile:data];

} else {

[self appendPostData:data];

}

i++;

// Only add the boundary if this is not the last item in the post body

if (i != [[self fileData] count]) {

[self appendPostString:endItemBoundary];

}

}

[self appendPostString:[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary]];

#if DEBUG_FORM_DATA_REQUEST

[self addToDebugBody:@"==== End of multipart/form-data body ====\r\n"];

#endif

}

- (void)buildURLEncodedPostBody

{

// We can't post binary data using application/x-www-form-urlencoded

if ([[self fileData] count] > 0) {

[self setPostFormat:ASIMultipartFormDataPostFormat];

[self buildMultipartFormDataPostBody];

return;

}

#if DEBUG_FORM_DATA_REQUEST

[self addToDebugBody:@"\r\n==== Building an application/x-www-form-urlencoded body ====\r\n"];

#endif

NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding]));

[self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]];

NSUInteger i=0;

NSUInteger count = [[self postData] count]-1;

for (NSDictionary *val in [self postData]) {

NSString *data = [NSString stringWithFormat:@"%@=%@%@", [self encodeURL:[val objectForKey:@"key"]], [self encodeURL:[val objectForKey:@"value"]],(i

[self appendPostString:data];

i++;

}

#if DEBUG_FORM_DATA_REQUEST

[self addToDebugBody:@"\r\n==== End of application/x-www-form-urlencoded body ====\r\n"];

#endif

}

- (void)appendPostString:(NSString *)string

{

#if DEBUG_FORM_DATA_REQUEST

[self addToDebugBody:string];

#endif

[super appendPostData:[string dataUsingEncoding:[self stringEncoding]]];

}

#if DEBUG_FORM_DATA_REQUEST

- (void)appendPostData:(NSData *)data

{

[self addToDebugBody:[NSString stringWithFormat:@"[%lu bytes of data]",(unsigned long)[data length]]];

[super appendPostData:data];

}

- (void)appendPostDataFromFile:(NSString *)file

{

NSError *err = nil;

unsigned long long fileSize = [[[[[[NSFileManager alloc] init] autorelease] attributesOfItemAtPath:file error:&err] objectForKey:NSFileSize] unsignedLongLongValue];

if (err) {

[self addToDebugBody:[NSString stringWithFormat:@"[Error: Failed to obtain the size of the file at '%@']",file]];

} else {

[self addToDebugBody:[NSString stringWithFormat:@"[%llu bytes of data from file '%@']",fileSize,file]];

}

[super appendPostDataFromFile:file];

}

- (void)addToDebugBody:(NSString *)string

{

if (string) {

[self setDebugBodyString:[[self debugBodyString] stringByAppendingString:string]];

}

}

#endif

#pragma mark NSCopying

- (id)copyWithZone:(NSZone *)zone

{

ASIFormDataRequest *newRequest = [super copyWithZone:zone];

[newRequest setPostData:[[[self postData] mutableCopyWithZone:zone] autorelease]];

[newRequest setFileData:[[[self fileData] mutableCopyWithZone:zone] autorelease]];

[newRequest setPostFormat:[self postFormat]];

[newRequest setStringEncoding:[self stringEncoding]];

[newRequest setRequestMethod:[self requestMethod]];

return newRequest;

}

@synthesize postData;

@synthesize fileData;

@synthesize postFormat;

@synthesize stringEncoding;

#if DEBUG_FORM_DATA_REQUEST

@synthesize debugBodyString;

#endif

@end

 

查看原文