Header file
#import
@interface EPUploader : NSObject { NSURL *serverURL; NSData *file; id delegate; SEL doneSelector; SEL errorSelector; BOOL uploadDidSucceed;}- (id)initWithURL: (NSURL *)serverURL file: (NSData *)file delegate: (id)delegate doneSelector: (SEL)doneSelector errorSelector: (SEL)errorSelector;- (NSData *)file;@end
Implementation File
#import "EPUploader.h"#import
static NSString * const BOUNDRY = @"0xKhTmLbOuNdArY";static NSString * const FORM_FLE_INPUT = @"uploaded";#define ASSERT(x) NSAssert(x, @"")@interface EPUploader (Private)- (void)upload;- (NSURLRequest *)postRequestWithURL: (NSURL *)url boundry: (NSString *)boundry data: (NSData *)data;//- (NSData *)compress: (NSData *)data;- (void)uploadSucceeded: (BOOL)success;- (void)connectionDidFinishLoading:(NSURLConnection *)connection;@end@implementation EPUploader/*-----------------------------------------------------------------------------* -[Uploader initWithURL:filePath:delegate:doneSelector:errorSelector:] --** Initializer. Kicks off the upload. Note that upload will happen on a* separate thread.** Results:An instance of Uploader. Side effects: None*-----------------------------------------------------------------------------*/- (id)initWithURL: (NSURL *)aServerURL // IN file: (NSData *)aFile // IN delegate: (id)aDelegate // IN doneSelector: (SEL)aDoneSelector // IN errorSelector: (SEL)anErrorSelector // IN{ if ((self = [super init])) { ASSERT(aServerURL); ASSERT(aFile); ASSERT(aDelegate); ASSERT(aDoneSelector); ASSERT(anErrorSelector); serverURL = aServerURL; file = aFile; delegate = aDelegate; doneSelector = aDoneSelector; errorSelector = anErrorSelector; [self upload]; } return self;}/**-----------------------------------------------------------------------------* -[Uploader dealloc] --** Destructor.** Results: None Side effects: None*----------------------------------------------------------------------------*/- (void)dealloc{ serverURL = nil; file = nil; delegate = nil; doneSelector = NULL; errorSelector = NULL; }/*-----------------------------------------------------------------------------* -[Uploader filePath] --** Gets the path of the file this object is uploading.** Results: Path to the upload file. Side effects: None*---------------------------------------------------------------------------*/- (NSData *)file{ return file;}@end // Uploader@implementation EPUploader (Private)/*-----------------------------------------------------------------------------* -[Uploader(Private) upload] --** Uploads the given file. The file is compressed before beign uploaded.* The data is uploaded using an HTTP POST command.** Results: None Side effects: None*---------------------------------------------------------------------------*/- (void)upload{ ASSERT(file); if (!file) { [self uploadSucceeded:NO]; return; } if ([file length] == 0) { // There's no data, treat this the same as no file. [self uploadSucceeded:YES]; return; } // NSData *compressedData = [self compress:data]; // ASSERT(compressedData && [compressedData length] != 0); // if (!compressedData || [compressedData length] == 0) { // [self uploadSucceeded:NO]; // return; // } NSURLRequest *urlRequest = [self postRequestWithURL:serverURL boundry:BOUNDRY data:file]; if (!urlRequest) { [self uploadSucceeded:NO]; return; } NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self]; if (!connection) { [self uploadSucceeded:NO]; } // Now wait for the URL connection to call us back.}/*-----------------------------------------------------------------------------* -[Uploader(Private) postRequestWithURL:boundry:data:] --** Creates a HTML POST request.** Results: The HTML POST request. Side effects: None*---------------------------------------------------------------------------*/- (NSURLRequest *)postRequestWithURL: (NSURL *)url // IN boundry: (NSString *)boundry // IN data: (NSData *)data // IN{ // from http://www.cocoadev.com/index.pl?HTTPFileUpload NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; [urlRequest setHTTPMethod:@"POST"]; [urlRequest setValue: [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundry] forHTTPHeaderField:@"Content-Type"]; NSNumber *ranNum = [NSNumber numberWithInt:arc4random() % 1000000]; NSMutableData *postData = [NSMutableData dataWithCapacity:[data length] + 512]; [postData appendData: [[NSString stringWithFormat:@"--%@\r\n", boundry] dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData: [[NSString stringWithFormat: @"Content-Disposition: form-data; name=\"%@\"; filename=\"image%@.png\"\r\n\r\n", FORM_FLE_INPUT,ranNum] dataUsingEncoding:NSUTF8StringEncoding]]; [postData appendData:data]; [postData appendData: [[NSString stringWithFormat:@"\r\n--%@--\r\n", boundry] dataUsingEncoding:NSUTF8StringEncoding]]; [urlRequest setHTTPBody:postData]; return urlRequest;}/*-----------------------------------------------------------------------------* -[Uploader(Private) compress:] --** Uses zlib to compress the given data.** Results: The compressed data as a NSData object. Side effects: None*------------------------------------------------------------------------------ (NSData *)compress: (NSData *)data // IN{if (!data || [data length] == 0)return nil;// zlib compress doc says destSize must be 1% + 12 bytes greater than source.uLong destSize = [data length] * 1.001 + 12;NSMutableData *destData = [NSMutableData dataWithLength:destSize];int error = compress([destData mutableBytes],&destSize,[data bytes],[data length]);if (error != Z_OK) {NSLog(@"%s: self:0x%p, zlib error on compress:%d\n",__func__, self, error);return nil;}[destData setLength:destSize];return destData;}*//*-----------------------------------------------------------------------------* -[Uploader(Private) uploadSucceeded:] --** Used to notify the delegate that the upload did or did not succeed.** Results: None Side effects: None*---------------------------------------------------------------------------*/- (void)uploadSucceeded: (BOOL)success // IN{ [delegate performSelector:success ? doneSelector : errorSelector withObject:self];}/*-----------------------------------------------------------------------------* -[Uploader(Private) connectionDidFinishLoading:] --** Called when the upload is complete. We judge the success of the upload* based on the reply we get from the server.** Results: None Side effects: None*---------------------------------------------------------------------------*/- (void)connectionDidFinishLoading:(NSURLConnection *)connection // IN{ NSLog(@"%s: self:0x%p\n", __func__, self); [self uploadSucceeded:uploadDidSucceed];}/*-----------------------------------------------------------------------------* -[Uploader(Private) connection:didFailWithError:] --** Called when the upload failed (probably due to a lack of network* connection).** Results: None Side effects: None*-----------------------------------------------------------------------------*/- (void)connection:(NSURLConnection *)connection // IN didFailWithError:(NSError *)error // IN{ NSLog(@"%s: self:0x%p, connection error:%s\n", __func__, self, [[error description] UTF8String]); [self uploadSucceeded:NO];}/**-----------------------------------------------------------------------------* -[Uploader(Private) connection:didReceiveResponse:] --** Called as we get responses from the server.** Results: None Side effects: None*---------------------------------------------------------------------------*/-(void) connection:(NSURLConnection *)connection // IN didReceiveResponse:(NSURLResponse *)response // IN{ NSLog(@"%s: self:0x%p\n", __func__, self);}/*-----------------------------------------------------------------------------* -[Uploader(Private) connection:didReceiveData:] --** Called when we have data from the server. We expect the server to reply* with a "YES" if the upload succeeded or "NO" if it did not.** Results: None Side effects: None*---------------------------------------------------------------------------*/- (void)connection:(NSURLConnection *)connection // IN didReceiveData:(NSData *)data // IN{ NSLog(@"%s: self:0x%p\n", __func__, self); NSString *reply = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%s: data: %s\n", __func__, [reply UTF8String]); if ([reply hasPrefix:@"YES"]) { uploadDidSucceed = YES; }}@end