记一次对Anyview阅读iOS版wifi传书功能的抹黑探索

记一次对Anyview阅读iOS版wifi传书功能的抹黑探索


原因

由于我最习惯使用的看小说的软件是 Anyview 阅读 ,当然也是习惯使然,从塞班时代,到安卓,到iOS,一直如此。

而我习惯于阅读本地书籍,即从网络下载之后,不用网也能看,主要是能一气呵成,看书要停顿,不太好。但是iOS有个不太友好的地方,就是不能随便操作本地文件,当然本着没有条件创造条件也要上的原则,使用wifi传输文件,就成为了一种折中的选择。

对于wifi传书的基本,在上篇博客iOS实现wifi传书功能中已经说过了。

但是在真实项目中使用,实在是毫无体验可言。我试着做过,靠着一点前端的基础,基本做了个demo,但实在拿不出手。


试错
  1. 从实现获取文件目录开始,使用jquery的ajax get请求,因为要实现删除的功能,所以返回的html中带了一个调用当前js的方法的button,(这个方法确实太傻),但是调用时却不执行,于是返回时将button改成a标签,之后就可以了

  2. 最关键的还是文件上传,在之前博客中,是可以文件上传的,但是上传之后,页面跳转,这就太丑了,对于只有几个简单功能来说的服务器来说,体验太差了。

    之后找到了一种可以实现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];
}

你可能感兴趣的:(记一次对Anyview阅读iOS版wifi传书功能的抹黑探索)