209 lines
7.9 KiB
Objective-C
Executable File
209 lines
7.9 KiB
Objective-C
Executable File
|
|
#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<HTTPResponse> *)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:@"<a href=\"%@\"> %@ </a><br/>",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
|