WiFi文件上传框架SGWiFiUpload
来源:互联网 发布:苹果mac下载哪些软件 编辑:程序博客网 时间:2024/06/04 00:27
背景
在iOS端由于文件系统的封闭性,文件的上传变得十分麻烦,一个比较好的解决方案是通过局域网WiFi来传输文件并存储到沙盒中。
简介
SGWiFiUpload是一个基于CocoaHTTPServer的WiFi上传框架。CocoaHTTPServer是一个可运行于iOS和OS X上的轻量级服务端框架,可以处理GET和POST请求,通过对代码的初步改造,实现了iOS端的WiFi文件上传与上传状态监听。
下载与使用
目前已经做成了易用的框架,上传到了GitHub,点击这里进入,欢迎Star!
请求的处理
CocoaHTTPServer通过HTTPConnection这一接口实现类来回调网络请求的各个状态,包括对请求头、响应体的解析等。为了实现文件上传,需要自定义一个继承HTTPConnection的类,这里命名为SGHTTPConnection
,与文件上传有关的几个方法如下。
解析文件上传的请求头
- (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; } // 这里用于发出文件开始上传的通知 dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:SGFileUploadDidStartNotification object:@{@"fileName" : filename ?: @"File"}]; }); // 这里用于设置文件的保存路径,先预存一个空文件,然后进行追加写内容 NSString *uploadDirPath = [SGWiFiUploadManager sharedManager].savePath; BOOL isDir = YES; if (![[NSFileManager defaultManager]fileExistsAtPath:uploadDirPath isDirectory:&isDir ]) { [[NSFileManager defaultManager]createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil]; } NSString* filePath = [uploadDirPath stringByAppendingPathComponent: filename]; if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ) { storeFile = nil; } else { HTTPLogVerbose(@"Saving file to %@", filePath); if(![[NSFileManager defaultManager] createDirectoryAtPath:uploadDirPath 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. // 由于除去文件内容外,还有HTML内容和空文件通过此方法处理,因此需要过滤掉HTML和空文件内容 if (!header.fields[@"Content-Disposition"]) { return; } else { MultipartMessageHeaderField *field = header.fields[@"Content-Disposition"]; NSString *fileName = field.params[@"filename"]; if (fileName.length == 0) return; } self.currentLength += data.length; CGFloat progress; if (self.contentLength == 0) { progress = 1.0f; } else { progress = (CGFloat)self.currentLength / self.contentLength; } dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:SGFileUploadProgressNotification object:@{@"progress" : @(progress)}]; }); if (storeFile) { [storeFile writeData:data]; }}
这里除了拼接文件内容以外,还发出了上传进度的通知,当前方法中只能拿到这一段文件的长度,总长度需要通过下面的方法拿到。
获取文件大小
- (void)prepareForBodyWithSize:(UInt64)contentLength{ HTTPLogTrace(); // 设置文件总大小,并初始化当前已经传输的文件大小。 self.contentLength = contentLength; self.currentLength = 0; // set up mime parser NSString* boundary = [request headerField:@"boundary"]; parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding]; parser.delegate = self; uploadedFiles = [[NSMutableArray alloc] init];}
处理传输完毕
- (void) processEndOfPartWithHeader:(MultipartMessageHeader*) header{ // as the file part is over, we close the file. // 由于除去文件内容外,还有HTML内容和空文件通过此方法处理,因此需要过滤掉HTML和空文件内容 if (!header.fields[@"Content-Disposition"]) { return; } else { MultipartMessageHeaderField *field = header.fields[@"Content-Disposition"]; NSString *fileName = field.params[@"filename"]; if (fileName.length == 0) return; } [storeFile closeFile]; storeFile = nil; dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:SGFileUploadDidEndNotification object:nil]; });}
这里关闭了文件管道,并且发出了文件上传完毕的通知。
开启Server
CocoaHTTPServer默认的Web根目录为MainBundle,他会在目录下寻找index.html,文件上传的请求地址为upload.html,当以POST方式请求upload.html时,请求会被Server拦截,并且交由HTTPConnection处理。
- (BOOL)startHTTPServerAtPort:(UInt16)port { HTTPServer *server = [HTTPServer new]; server.port = port; self.httpServer = server; [self.httpServer setDocumentRoot:self.webPath]; [self.httpServer setConnectionClass:[SGHTTPConnection class]]; NSError *error = nil; [self.httpServer start:&error]; return error == nil;}
在HTML中发送POST请求上传文件
在CocoaHTTPServer给出的样例中有用于文件上传的index.html,要实现文件上传,只需要一个POST方法的form表单,action为upload.html,每一个文件使用一个input标签,type为file即可,这里为了美观对input标签进行了自定义。
下面的代码演示了能同时上传3个文件的index.html代码。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html> <head> <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"> </head> <style> body { margin: 0px; padding: 0px; font-size: 12px; background-color: rgb(244,244,244); text-align: center; } #container { margin: auto; } #form { margin-top: 60px; } .upload { margin-top: 2px; } #submit input { background-color: #ea4c88; color: #eee; font-weight: bold; margin-top: 10px; text-align: center; font-size: 16px; border: none; width: 120px; height: 36px; } #submit input:hover { background-color: #d44179; } #submit input:active { background-color: #a23351; } .uploadField { margin-top: 2px; width: 200px; height: 22px; font-size: 12px; } .uploadButton { background-color: #ea4c88; color: #eee; font-weight: bold; text-align: center; font-size: 15px; border: none; width: 80px; height: 26px; } .uploadButton:hover { background-color: #d44179; } .uploadButton:active { background-color: #a23351; } </style> <body> <div id="container"> <div id="form"> <h2>WiFi File Upload</h2> <form name="form" action="upload.html" method="post" enctype="multipart/form-data" accept-charset="utf-8"> <div class="upload"> <input type="file" name="upload1" id="upload1" style="display:none" onChange="document.form.path1.value=this.value"> <input class="uploadField" name="path1" readonly> <input class="uploadButton" type="button" value="Open" onclick="document.form.upload1.click()"> </div> <div class="upload"> <input type="file" name="upload2" id="upload2" style="display:none" onChange="document.form.path2.value=this.value"> <input class="uploadField" name="path2" readonly> <input class="uploadButton" type="button" value="Open" onclick="document.form.upload2.click()"> </div> <div class="upload"> <input type="file" name="upload3" id="upload3" style="display:none" onChange="document.form.path3.value=this.value"> <input class="uploadField" name="path3" readonly> <input class="uploadButton" type="button" value="Open" onclick="document.form.upload3.click()"> </div> <div id="submit"><input type="submit" value="Submit"></div> </form> </div> </div> </body></html>
表单提交后,会进入upload.html页面,该页面用于说明上传完毕,下面的代码实现了3秒后的重定向返回。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html> <head> <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"> <meta http-equiv=refresh content="3;url=index.html"> </head> <body> <h3>Upload Succeeded!</h3> <p>The Page will be back in 3 seconds</p> </body></html>
- WiFi文件上传框架SGWiFiUpload
- wifi下上传文件
- spring框架上传文件
- CI框架文件上传
- ExtJs框架 文件上传
- 文件上传-Struts2框架
- 文件上传(Struts框架)
- yii框架文件上传
- Play框架文件上传
- Struts2 框架文件上传
- yii框架文件上传
- Struts2框架的文件上传
- struts框架-实现文件上传
- dwz框架做文件上传
- struts2框架-------文件上传机制
- JFinal 框架的文件上传
- zhphp框架 文件上传类
- play框架之文件上传
- wampserver 多个PHP版本配置后,无法启动
- 清空svn的队列, svn cleanup failed–previous operation has not finished; run cleanup if it was interrupted
- lower_bound & upper_bound
- httpclient.execute长时间停滞问题
- 上拉刷新,下拉加载(二)
- WiFi文件上传框架SGWiFiUpload
- java中通过包名获取该包下的所有class
- U3D学习日记一:坦克大战,初步建立场景
- CUDA5.5+vs2010 环境配置 7.5也可以使用 亲自试过
- 在Canvas中利用Path绘制基本图形
- 从ViewController初始化一直谈到强制横屏
- django windos环境下配置成功但无法创建文件
- ARM MMU工作原理剖析
- 浅谈卡尔曼滤波