寫在前面:
在iOS開發的過程中,有很多時候我們都在和數據打交道,最基本的就是數據的下載和上傳了,估計很多很多的小伙伴都在用AFNetworking與后台數據打交道,可有沒有想過,哪天AFNetworking你不能用了或者不會用了怎么辦?可能你心中疑惑了,這三方只要更新,存在怎么會不能用或者我怎么會不會用了,在沒有看Telegram源碼之前,我也是這么想的,看了Telegram源碼就不會再這么想了,以后我會把自己看的Telegram源碼部分的總結和經驗一點點的整理分享出來,整理成這個Telegram學習解析系列,有興趣的同行可以加文章鏈接最后面的telegram開發學習群,一起學習討論Telegram問題,Android和iOS都可以。一起進步!
需求怎樣來的?
先看看這個,在Telegram的安全協議 MtProtoKit中,你可以看到這個Third Party 這個文件,看下面的截圖:

可以看到這里面是有AFNetworking的,這個框架里面的東西有寫就是集成字AF來寫的,但AF這個版本是挺低的,嘗試着自己在這個基礎上去寫上傳那些方法應該是可以,我嘗試過之后放棄了,還是決定利用 NSURLConnection / NSURLSessionDataTask來自己寫,不過這個的話就的涉及到了請求這些東西的一個封裝,以及利用這個上傳圖片或者語音什么的時候,還有里面的參數的一個組裝,接下來就認真的把這部分的東西寫出來,這也是在Telegram的基礎上衍生出來的問題,要是平常的項目中,可能也不會輕易涉及到這些東西,既然用到了就好好總結一下:
一:簡單的數據訪問
先從簡單的開始,就從你給后台Post數據開始,先從NSURLConnection開始,下面的代碼就是具體的實例,每一句都有具體的注釋,看代碼:
-(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部分的我們就不說了,和上面的一樣,參考上面的就行,下面就是一個完整的方法,你通過請求獲取到數據回調的方法:
-(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消息類型上傳數據給后台的時候添加的,這個方法可能里面納西而判斷等等的東西你用不着,主要的你看里面上傳部分的內容封裝吧,主要的還是這部分的東西,或者對這個方法里面還有什么疑問的,可以問我。方法我直接給出來,里面的注釋真的挺詳細的了。一句一句的過:
/**
上傳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;
}
為了不讓博客篇幅太長,上面涉及到的視頻壓縮,還有文件的后綴名的判斷方法就不在發出來了,到時這個消息類型的判斷,這個我覺得是有必要發出來的,不是說這個有多復雜,只是可能找起來沒那么容易能找打一份完整的,既然能看到這,估計可能有伙伴會有需要的:
/*** 根據文件類型判斷上傳的文件格式 ***/
+(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;
}
