#import "MyHTTPConnection.h" #import "HTTPMessage.h" #import "HTTPDataResponse.h" #import "DDNumber.h" #import "HTTPLogging.h" #import "MultipartFormDataParser.h" #import "MultipartMessageHeaderField.h" #import "HTTPDynamicFileResponse.h" #import "HTTPFileResponse.h" // Log levels : off, error, warn, info, verbose // Other flags: trace static const int httpLogLevel = HTTP_LOG_LEVEL_VERBOSE; // | HTTP_LOG_FLAG_TRACE; /** * All we have to do is override appropriate methods in HTTPConnection. **/ @implementation MyHTTPConnection - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path { HTTPLogTrace(); // Add support for POST if ([method isEqualToString:@"POST"]) { if ([path isEqualToString:@"/upload.html"]) { return YES; } } return [super supportsMethod:method atPath:path]; } - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path { HTTPLogTrace(); // Inform HTTP server that we expect a body to accompany a POST request if([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.html"]) { // here we need to make sure, boundary is set in header NSString* contentType = [request headerField:@"Content-Type"]; NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location; if( NSNotFound == paramsSeparator ) { return NO; } if( paramsSeparator >= contentType.length - 1 ) { return NO; } NSString* type = [contentType substringToIndex:paramsSeparator]; if( ![type isEqualToString:@"multipart/form-data"] ) { // we expect multipart/form-data content type return NO; } // enumerate all params in content-type, and find boundary there NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"]; for( NSString* param in params ) { paramsSeparator = [param rangeOfString:@"="].location; if( (NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1 ) { continue; } NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)]; NSString* paramValue = [param substringFromIndex:paramsSeparator+1]; if( [paramName isEqualToString: @"boundary"] ) { // let's separate the boundary from content-type, to make it more handy to handle [request setHeaderField:@"boundary" value:paramValue]; } } // check if boundary specified if( nil == [request headerField:@"boundary"] ) { return NO; } return YES; } return [super expectsRequestBodyFromMethod:method atPath:path]; } - (NSObject *)httpResponseForMethod:(NSString *)method URI:(NSString *)path { HTTPLogTrace(); if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.html"]) { // this method will generate response with links to uploaded file NSMutableString* filesStr = [[NSMutableString alloc] init]; for( NSString* filePath in uploadedFiles ) { //generate links [filesStr appendFormat:@" %@
",filePath, [filePath lastPathComponent]]; } NSString* templatePath = [[config documentRoot] stringByAppendingPathComponent:@"upload.html"]; NSDictionary* replacementDict = [NSDictionary dictionaryWithObject:filesStr forKey:@"MyFiles"]; // use dynamic file response to apply our links to response template return [[HTTPDynamicFileResponse alloc] initWithFilePath:templatePath forConnection:self separator:@"%" replacementDictionary:replacementDict]; } if( [method isEqualToString:@"GET"] && [path hasPrefix:@"/upload/"] ) { // let download the uploaded files return [[HTTPFileResponse alloc] initWithFilePath: [[config documentRoot] stringByAppendingString:path] forConnection:self]; } return [super httpResponseForMethod:method URI:path]; } - (void)prepareForBodyWithSize:(UInt64)contentLength { HTTPLogTrace(); // set up mime parser NSString* boundary = [request headerField:@"boundary"]; parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding]; parser.delegate = self; uploadedFiles = [[NSMutableArray alloc] init]; } - (void)processBodyData:(NSData *)postDataChunk { HTTPLogTrace(); // append data to the parser. It will invoke callbacks to let us handle // parsed data. [parser appendData:postDataChunk]; } //----------------------------------------------------------------- #pragma mark multipart form data parser delegate - (void) processStartOfPartWithHeader:(MultipartMessageHeader*) header { // in this sample, we are not interested in parts, other then file parts. // check content disposition to find out filename MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"]; NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent]; if ( (nil == filename) || [filename isEqualToString: @""] ) { // it's either not a file part, or // an empty form sent. we won't handle it. return; } // 在这里修改文件存储的位置 NSAssert([self.delegate respondsToSelector:@selector(onSetDestinationPathHttpFileTranSportDestination:)], @"Need to add delegate name onSetDestinationPathHttpFileTranSportDestination to add a destinationPath"); if ([self.delegate respondsToSelector:@selector(onSetDestinationPathHttpFileTranSportDestination:)]) { self.destinationPath = [self.delegate onSetDestinationPathHttpFileTranSportDestination:self]; NSAssert(self.destinationPath.length > 0 || self.destinationPath, @"destinationPath can not be nil"); } //拼接文件名或者预计文件路径 NSString* filePath = [self.destinationPath stringByAppendingPathComponent: filename]; self.filePath = filePath; //需要判断是否存在重复文件 if ([self.delegate respondsToSelector:@selector(onHttpFileDataEstimateDuplicateCanPassTranSportServer:withPath:andFileName:)]) { if (![self.delegate onHttpFileDataEstimateDuplicateCanPassTranSportServer:self withPath:self.filePath andFileName:filename]) { return; }; } HTTPLogVerbose(@"Saving file to %@", filePath); if(![[NSFileManager defaultManager] createDirectoryAtPath:self.destinationPath withIntermediateDirectories:true attributes:nil error:nil]) { HTTPLogError(@"Could not create directory at path: %@", filePath); } if(![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]) { HTTPLogError(@"Could not create file at path: %@", filePath); } storeFile = [NSFileHandle fileHandleForWritingAtPath:filePath]; [uploadedFiles addObject: [NSString stringWithFormat:@"/upload/%@", filename]]; } - (void) processContent:(NSData*) data WithHeader:(MultipartMessageHeader*) header { // here we just write the output from parser to the file. if( storeFile ) { [storeFile writeData:data]; } } - (void) processEndOfPartWithHeader:(MultipartMessageHeader*) header { // as the file part is over, we close the file. [storeFile closeFile]; storeFile = nil; } - (void) processPreambleData:(NSData*) data { // if we are interested in preamble data, we could process it here. } - (void) processEpilogueData:(NSData*) data { // if we are interested in epilogue data, we could process it here. //用户更新歌曲数量 [[NSNotificationCenter defaultCenter] postNotificationName:@"processEpilogueData" object:nil]; if ([self.delegate respondsToSelector:@selector(onHttpFileTranSportServer:successWithPath:)]) { [self.delegate onHttpFileTranSportServer:self successWithPath:self.filePath]; } } @end