Telegram学习解析系列(二):这我怎么给后台传输数据?
写在前面:
在iOS开发的过程中,有很多时候我们都在和数据打交道,最基本的就是数据的下载和上传了,估计很多很多的小伙伴都在用AFNetworking与后台数据打交道,可有没有想过,哪天AFNetworking你不能用了或者不会用了怎么办?可能你心中疑惑了,这三方只要更新,存在怎么会不能用或者我怎么会不会用了,在没有看Telegram源码之前,我也是这么想的,看了Telegram源码就不会再这么想了,以后我会把自己看的Telegram源码部分的总结和经验一点点的整理分享出来,整理成这个Telegram学习解析系列,有兴趣的同行可以加文章链接最后面的telegram开发学习群,一起学习讨论Telegram问题,Android和iOS都可以。一起进步!
需求怎样来的?
先看看这个,在Telegram的安全协议 MtProtoKit中,你可以看到这个Third Party 这个文件,看下面的截图:
可以看到这里面是有AFNetworking的,这个框架里面的东西有写就是集成字AF来写的,但AF这个版本是挺低的,尝试着自己在这个基础上去写上传那些方法应该是可以,我尝试过之后放弃了,还是决定利用 NSURLConnection / NSURLSessionDataTask来自己写,不过这个的话就的涉及到了请求这些东西的一个封装,以及利用这个上传图片或者语音什么的时候,还有里面的参数的一个组装,接下来就认真的把这部分的东西写出来,这也是在Telegram的基础上衍生出来的问题,要是平常的项目中,可能也不会轻易涉及到这些东西,既然用到了就好好总结一下:
一:简单的数据访问
先从简单的开始,就从你给后台Post数据开始,先从NSURLConnection开始,下面的代码就是具体的实例,每一句都有具体的注释,看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | -( NSString * )httpRequestWithParameters:( NSDictionary *)dict andURL:( NSURL *)url{ //把参数字典转化成Data NSData * postData = [ NSJSONSerialization dataWithJSONObject:dict options:0 error: NULL ]; //把Data利用这个Key加密,这个Key自己设置 NSString * key = @ "********" ; postData = [postData AES256_Encrypt:key]; //初始化request NSMutableURLRequest *request = [ NSMutableURLRequest requestWithURL:url]; //设置请求方式 [request setHTTPMethod:@ "POST" ]; //添加请求体,这里要进行64编码处理,就是这个newStringInBase64FromData方法 [request setHTTPBody:[[postData newStringInBase64FromData] dataUsingEncoding: NSUTF8StringEncoding ]]; //设置请求的报文 [request setValue:@ "utf-8" forHTTPHeaderField:@ "charset" ]; [request setValue:@ "application/x-www-form-urlencoded" forHTTPHeaderField:@ "Content-Type" ]; //请求超时时间设置 [request setTimeoutInterval:15.0]; NSOperationQueue * queue = [[ NSOperationQueue alloc]init]; [ NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^( NSURLResponse * response, NSData *data, NSError *error){ if (error){ NSLog (@ "文本内容上传失败" ); NSLog (@ "%@" ,data); NSLog (@ "%@" ,response); } else { // 解析服务器返回的数据(解析成字符串) NSString *string = [[ NSString alloc] initWithData:data encoding: NSUTF8StringEncoding ]; NSLog (@ "解析服务器返回的数据====%@" , string); } }]; } |
注意:关于配置报文下面这篇文章 POST请求的forHTTPHeaderField 感谢作者。
上面方法那些编码、加密方法,你要有需要的话可以在我首页找到我Q,我发给你。
上面的方法你可以给后台去POST数据,再说剩下的这个 NSURLSessionDataTask ,其实苹果是不建议使用前面的 NSURLConnection 了的,这个我们就说的简单点,你怎么从后台请求数据,下面就但是一个简单的Get方法,请求Request部分的我们就不说了,和上面的一样,参考上面的就行,下面就是一个完整的方法,你通过请求获取到数据回调的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | -( void )httpRequestWithURL:( NSURL *)url andHttpRequestSuccess:(HttpRequestSuccess)httpRequestSuccess andHttpRequestFail:(HttpRequestFail)httpRequestFail{ //推荐使用这种请求方法,上面的方已经被废弃 //下面的方法没有给Request设置请求头和内容,有需要参考上面的写法 NSURLSession * session = [ NSURLSession sharedSession]; NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:[ NSURLRequest requestWithURL:url] completionHandler:^( NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (!error) { //没有错误,返回正确 NSError * jsonError; NSDictionary * dic =[ NSJSONSerialization JSONObjectWithData:data options: NSJSONReadingMutableContainers error:&jsonError]; if (!jsonError) { httpRequestSuccess(dic); } } else { //请求出现错误 httpRequestFail(@ "请求错误" ); } NSLog (@ "response==%@" ,response); }]; [dataTask resume]; } |
上面的这些就把简单的怎样和后台进行数据交互就解决了,当然这试试简单的,涉及到文件下载上传的我们就下面接着说:
二 :涉及到文件类型的怎么处理
下面这个方法是在处理Telegram消息类型上传数据给后台的时候添加的,这个方法可能里面纳西而判断等等的东西你用不着,主要的你看里面上传部分的内容封装吧,主要的还是这部分的东西,或者对这个方法里面还有什么疑问的,可以问我。方法我直接给出来,里面的注释真的挺详细的了。一句一句的过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /** 上传Data @param url 上传DataUrl @param postParems 参数 @param picFilePath 文件路径 @param picFileName 文件名称, @param message_Type 消息类型(区分你要上传的文件是什么类型的,图片、视频、语音等等) @param fileName 这是像PDF,TXT等格式问文件的文件名 @return return value description */ + ( NSString *)postRequestWithURL: ( NSString *)url postParems: ( NSMutableDictionary *)postParems picFilePath: ( NSString *)picFilePath picFileName: ( NSString *)picFileName andMessageType:(Message_Type)message_Type andFileName:( NSString *)fileName{ /** boundary: 是分隔符号,告诉服务器,我的请求体里用的就是就是这个分隔符,而且,拼接请求体也用到这个分隔符 */ NSString *TWITTERFON_FORM_BOUNDARY = @ "iOSFileUploaded" ; //根据url初始化request NSMutableURLRequest * request = [ NSMutableURLRequest requestWithURL:[ NSURL URLWithString:url] cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10]; //分界线 --AaB03x NSString *MPboundary=[[ NSString alloc]initWithFormat:@ "--%@" ,TWITTERFON_FORM_BOUNDARY]; //结束符 AaB03x-- NSString *endMPboundary=[[ NSString alloc]initWithFormat:@ "%@--" ,MPboundary]; NSData * data; NSString * format; // 文件上传的格式 //得到图片的data if (message_Type == ImageMessage) { format = @ " image/jpge,image/gif, image/jpeg, image/pjpeg, image/pjpeg" ; UIImage *image=[UIImage imageWithContentsOfFile:picFilePath]; //返回为JPEG图像 data = UIImageJPEGRepresentation(image, 0.3f); //得到语音或者视频的data } else if (message_Type == VoiceMessage){ format = @ "audio/mp3" ; data= [ NSData dataWithContentsOfFile:picFilePath]; } else if (message_Type == VedioMessage){ format = @ "audio/mp4" ; [ self convertVideoWithModel:picFilePath andUrl:url andNSDictionary:postParems]; return @ "进入了视频压缩" ; } else if (message_Type == PasterMessage){ format = @ "image/webp" ; //webp图片格式 data = [ NSData dataWithContentsOfFile:picFilePath]; } else if (message_Type == FileMessage){ format = [ self GetContentType:fileName]; //判断文件的上传格式,利用后缀名判断 data = [ NSData dataWithContentsOfFile:picFilePath]; } // 在这里判断Data是否存在 if (!data) { NSLog (@ "要上传的data不存在" ); return @ "data不存在" ; } //http body的字符串 NSMutableString *body=[[ NSMutableString alloc]init]; //参数的集合的所有key的集合 NSArray *keys= [postParems allKeys]; //遍历keys for ( int i=0;i<( int )[keys count];i++){ //得到当前key NSString *key=[keys objectAtIndex:i]; //添加分界线,换行 [body appendFormat:@ "%@\r\n" ,MPboundary]; //添加字段名称,换2行 [body appendFormat:@ "Content-Disposition: form-data; name=\"%@\"\r\n\r\n" ,key]; //添加字段的值 [body appendFormat:@ "%@\r\n" ,[postParems objectForKey:key]]; } if (picFileName){ ////添加分界线,换行 [body appendFormat:@ "%@\r\n" ,MPboundary]; //声明pic字段,文件名为boris.png [body appendFormat:@ "Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n" ,FORM_FLE_INPUT,picFileName]; //声明上传文件的格式 NSString * formant = [ NSString stringWithFormat:@ "Content-Type:%@\r\n\r\n" ,format]; [body appendFormat:@ "%@" , formant]; } //声明结束符:--AaB03x-- NSString *end=[[ NSString alloc]initWithFormat:@ "\r\n%@" ,endMPboundary]; //声明myRequestData,用来放入http body NSMutableData *myRequestData=[ NSMutableData data]; //将body字符串转化为UTF8格式的二进制 [myRequestData appendData:[body dataUsingEncoding: NSUTF8StringEncoding ]]; if (data){ [myRequestData appendData:data]; } //加入结束符--AaB03x-- [myRequestData appendData:[end dataUsingEncoding: NSUTF8StringEncoding ]]; //设置HTTPHeader中Content-Type的值 NSString *content=[[ NSString alloc]initWithFormat:@ "multipart/form-data; boundary=%@" ,TWITTERFON_FORM_BOUNDARY]; //设置HTTPHeader [request setValue:content forHTTPHeaderField:@ "Content-Type" ]; //设置Content-Length [request setValue:[ NSString stringWithFormat:@ "%lu" , (unsigned long )[myRequestData length]] forHTTPHeaderField:@ "Content-Length" ]; //设置http body [request setHTTPBody:myRequestData]; //http method [request setHTTPMethod:@ "POST" ]; NSHTTPURLResponse *urlResponese = nil ; NSError * error = [[ NSError alloc]init]; NSData * resultData = [ NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponese error:&error]; NSDictionary * JSONresponseObject = [ NSJSONSerialization JSONObjectWithData:resultData options: NSJSONReadingMutableContainers error: nil ]; if ([[ NSString stringWithFormat:@ "%@" ,JSONresponseObject[@ "errorCode" ]] isEqualToString:@ "0" ]) { NSString *string = [[ NSString alloc] initWithData:resultData encoding: NSUTF8StringEncoding ]; NSLog (@ "解析服务器返回的字符串====%@" , string); NSLog (@ "解析服务器返回的字典 ====%@" , JSONresponseObject); return @ "200" ; } return nil ; } |
为了不让博客篇幅太长,上面涉及到的视频压缩,还有文件的后缀名的判断方法就不在发出来了,到时这个消息类型的判断,这个我觉得是有必要发出来的,不是说这个有多复杂,只是可能找起来没那么容易能找打一份完整的,既然能看到这,估计可能有伙伴会有需要的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | /*** 根据文件类型判断上传的文件格式 ***/ +( NSString *)GetContentType:( NSString *)filename{ // 判断之前先把文件名称转化成小写 NSString * Filename = [filename lowercaseString]; if ([Filename hasSuffix:@ "avi" ]) { return @ "video/avi" ; } else if ([Filename hasSuffix:@ "bmp" ]) { return @ "application/x-bmp" ; } else if ([Filename hasSuffix:@ "jpeg" ]) { return @ "image/jpeg" ; } else if ([Filename hasSuffix:@ "jpg" ]) { return @ "image/jpeg" ; } else if ([Filename hasSuffix:@ "png" ]) { return @ "image/x-png" ; } else if ([Filename hasSuffix:@ "mp3" ]) { return @ "audio/mp3" ; } else if ([Filename hasSuffix:@ "mp4" ]) { return @ "video/mpeg4" ; } else if ([Filename hasSuffix:@ "rmvb" ]) { return @ "application/vnd.rn-realmedia-vbr" ; } else if ([Filename hasSuffix:@ "txt" ]) { return @ "text/plain" ; } else if ([Filename hasSuffix:@ "xsl" ]) { return @ "application/x-xls" ; } else if ([Filename hasSuffix:@ "xslx" ]) { return @ "application/x-xls" ; } else if ([Filename hasSuffix:@ "xwd" ]) { return @ "application/x-xwd" ; } else if ([Filename hasSuffix:@ "doc" ]) { return @ "application/msword" ; } else if ([Filename hasSuffix:@ "docx" ]) { return @ "application/msword" ; } else if ([Filename hasSuffix:@ "ppt" ]) { return @ "application/x-ppt" ; } else if ([Filename hasSuffix:@ "pdf" ]) { return @ "application/pdf" ; } return nil ; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2016-05-12 Swift 实现俄罗斯方块详细思路解析(附完整项目)