记一次对Anyview阅读iOS版wifi传书功能的抹黑探索
原因
由于我最习惯使用的看小说的软件是 Anyview 阅读 ,当然也是习惯使然,从塞班时代,到安卓,到iOS,一直如此。
而我习惯于阅读本地书籍,即从网络下载之后,不用网也能看,主要是能一气呵成,看书要停顿,不太好。但是iOS有个不太友好的地方,就是不能随便操作本地文件,当然本着没有条件创造条件也要上的原则,使用wifi传输文件,就成为了一种折中的选择。
对于wifi传书的基本,在上篇博客iOS实现wifi传书功能中已经说过了。
但是在真实项目中使用,实在是毫无体验可言。我试着做过,靠着一点前端的基础,基本做了个demo,但实在拿不出手。
试错
从实现获取文件目录开始,使用jquery的ajax get请求,因为要实现删除的功能,所以返回的html中带了一个调用当前js的方法的button,(这个方法确实太傻),但是调用时却不执行,于是返回时将button改成a标签,之后就可以了
-
最关键的还是文件上传,在之前博客中,是可以文件上传的,但是上传之后,页面跳转,这就太丑了,对于只有几个简单功能来说的服务器来说,体验太差了。
之后找到了一种可以实现ajax上传的方式,使用Ajaxupload.js上传文件,但是上传之后成功的回调,却无法调用本地js,替换当前页面的数据,而且不能显示进度条。劣势自然而然。
最后让我想到一个方法,因为iOS app作为服务器,服务器文件只能放在ipa包内,所以一不做二不休,直接拆包,拿到 Anyview阅读 中的web服务器文件,然而拿到是拿到了,枉我还做过pc网站,竟然萌萌哒,看来js还是要多加学习呀。看来修改前端是行不通了,那就另寻他路。
他路
没那心思去滤清前端的逻辑,那就从中间作为入口,使用 Charles 监听接口,根据传过来的数据,处理之后,仿照之前的数据返回不就行了。于是,一切照章进行,除了下载功能,其他均已实现。
废话少说,当然是上代码
AnyHTTPConnection.m
- 设置支持的方法
- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
{
HTTPLogTrace();
// Add support for POST
if ([method isEqualToString:@"POST"])
{
return YES;
}
if ([method isEqualToString:@"DELETE"]) {
return YES;
}
if ([method isEqualToString:@"GET"]) {
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 hasPrefix:@"/upload"]) {
// 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];
}
- 处理body数据
- (void)processBodyData:(NSData *)postDataChunk
{
HTTPLogTrace();
// append data to the parser. It will invoke callbacks to let us handle
// parsed data.
[request appendData:postDataChunk];//post body存储
[parser appendData:postDataChunk];//文件存储
}
- 最关键的所在,处理请求
- (NSObject *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
HTTPLogTrace();
//页面初始化时,填充数据
if ([path isEqualToString:@"/index.html"] || [path isEqualToString:@"/"] || [path hasPrefix:@"/index.html"]) {
NSString* templatePath = [[config documentRoot] stringByAppendingPathComponent:@"index.html"];
NSMutableDictionary *replacementDict = [NSMutableDictionary dictionary];
[replacementDict setObject:@"Wifi传书" forKey:@"title"];
[replacementDict setObject:@"iOS" forKey:@"device"];
[replacementDict setObject:@"XFReader" forKey:@"header"];
[replacementDict setObject:@"拖拽文件到窗口或者点击“上传书籍”按钮选择您要上传的书籍,上传完成后就可以在书架上看到您上传的书啦,赶紧上传吧!" forKey:@"prologue"];
[replacementDict setObject:@"" forKey:@"epilogue"];
[replacementDict setObject:@"XFReader 1.0.0" forKey:@"footer"];
return [[HTTPDynamicFileResponse alloc] initWithFilePath:templatePath forConnection:self separator:@"%" replacementDictionary:replacementDict];
}
//表单数据,返回NSString类型json数据[{"name":"1.txt","path":"/upload/1.txt","size":"12400"},{"name":"2.txt","path":"/upload/2.txt","size":"15000"}]
if ([path hasPrefix:@"/list?path="] && [method isEqualToString:@"GET"]) {
NSData *response = [self dictToData:[self filesInfoList]];
return [[HTTPDataResponse alloc] initWithData:response];
}
//下载 返回 文件内容
if ([path hasPrefix:@"/download?path="] && [method isEqualToString:@"GET"]) {
//获取body内容
NSArray *tArr = [[path stringByRemovingPercentEncoding] componentsSeparatedByString:@"="];
if (tArr.count >= 2) {
//返回文件数据
//NSData *data = [NSData dataWithContentsOfFile:[self filePath:tArr[1]] options:(NSDataReadingUncached) error:nil];
//return [[HTTPDataResponse alloc] initWithData:data];
}else {
//参数错误
NSData *response = [@"参数错误" dataUsingEncoding:NSUTF8StringEncoding];
return [[HTTPDataResponse alloc] initWithData:response];
}
}
//删除 根据路径删除文件
if ([path hasPrefix:@"/delete"] && [method isEqualToString:@"POST"]) {
NSString *body = [[NSString alloc] initWithData:request.body encoding:NSUTF8StringEncoding];
NSArray *tArr = [[body stringByRemovingPercentEncoding] componentsSeparatedByString:@"="];
if (tArr.count >= 2) {
[self deleteFile:tArr[1]];//删除文件
}
NSData *response = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
return [[HTTPDataResponse alloc] initWithData:response];
}
//上传文件
if ([path hasPrefix:@"/upload"] && [method isEqualToString:@"POST"]) {
[self moveFiles:uploadedFiles];
NSData *response = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
return [[HTTPDataResponse alloc] initWithData:response];
}
//重命名 根据新旧路径 修改文件名 oldPath=/upload/1.txt&newPath=/upload/2.txt
if ([path hasPrefix:@"/move"] && [method isEqualToString:@"POST"]) {
//oldPath, newPath
NSString *body = [[[NSString alloc] initWithData:request.body encoding:NSUTF8StringEncoding] stringByRemovingPercentEncoding];
NSLog(@"body = %@",body);
NSArray *arr = [body componentsSeparatedByString:@"&"];//oldPath=/upload/1.txt
NSArray *tA0 = [arr[0] componentsSeparatedByString:@"="];//olodPath /upload/1.txt
NSArray *tA1 = [arr[1] componentsSeparatedByString:@"="];
NSString *oldPath = @"";
NSString *newPath = @"";
if ([tA0[0] isEqualToString:@"oldPath"]) {
oldPath = tA0[1];
newPath = tA1[1];
}else {
oldPath = tA1[1];
newPath = tA0[1];
}
[self renameFile:oldPath newPath:newPath];
NSData *response = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
return [[HTTPDataResponse alloc] initWithData:response];
}
return [super httpResponseForMethod:method URI:path];
}