-
1.1 JSON簡單介紹
001 什么是JSON
(1)JSON是一種輕量級的數據格式,一般用于數據交互
(2)服務器返回給客戶端的數據,一般都是JSON格式或者XML格式(文件下載除外)
002 相關說明
(1)JSON的格式很像OC中的字典和數組
(2)標準JSON格式key必須是雙引號
003 JSON解析方案
a.第三方框架 JSONKit\SBJSON\TouchJSON
b.蘋果原生(NSJSONSerialization) 1.2 JSON解析相關代碼
(1)json數據->OC對象
//把json數據轉換為OC對象
-(void)jsonToOC
{
//1. 確定url路徑
NSURL *url = [NSURL URLWithString:@"http://120.28.226.186:32812/login?username=33&pwd=33&type=JSON"];
//2.創建一個請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.使用NSURLSession發送一個異步請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//4.當接收到服務器響應的數據后,解析數據(JSON--->OC)
/*
第一個參數:要解析的JSON數據,是NSData類型也就是二進制數據
第二個參數: 解析JSON的可選配置參數
NSJSONReadingMutableContainers 解析出來的字典和數組是可變的
NSJSONReadingMutableLeaves 解析出來的對象中的字符串是可變的 iOS7以后有問題
NSJSONReadingAllowFragments 被解析的JSON數據如果既不是字典也不是數組, 那么就必須使用這個
*/
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}];
}
(2)OC對象->JSON對象
//1.要轉換成JSON數據的OC對象*這里是一個字典
NSDictionary *dictM = @{
@"name":@"cheng",
@"age":@100,
@"height":@1.72
};
//2.OC->JSON
/*
注意:可以通過+ (BOOL)isValidJSONObject:(id)obj;方法判斷當前OC對象能否轉換為JSON數據
具體限制:
1.obj 是NSArray 或 NSDictionay 以及他們派生出來的子類
2.obj 包含的所有對象是NSString,NSNumber,NSArray,NSDictionary 或NSNull
3.字典中所有的key必須是NSString類型的
4.NSNumber的對象不能是NaN或無窮大
*/
/*
第一個參數:要轉換成JSON數據的OC對象,這里為一個字典
第二個參數:NSJSONWritingPrettyPrinted對轉換之后的JSON對象進行排版,無意義
*/
NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];
//3.打印查看Data是否有值
/*
第一個參數:要轉換為STring的二進制數據
第二個參數:編碼方式,通常采用NSUTF8StringEncoding
*/
NSString *strM = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",strM);
(3)OC對象和JSON數據格式之間的一一對應關系
//OC對象和JSON數據之間的一一對應關系
-(void)oCWithJSON
{
//JSON的各種數據格式
//NSString *test = @"\"cheng\"";
//NSString *test = @"true";
NSString *test = @"{\"name\":\"cheng\"}";
//把JSON數據->OC對象,以便查看他們之間的一一對應關系
//注意點:如何被解析的JSON數據如果既不是字典也不是數組(比如是NSString), 那么就必須使用這NSJSONReadingAllowFragments
id obj = [NSJSONSerialization JSONObjectWithData:[test dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@", [obj class]);
/* JSON數據格式和OC對象的一一對應關系
{} -> 字典
[] -> 數組
"" -> 字符串
10/10.1 -> NSNumber
true/false -> NSNumber
null -> NSNull
*/
}
}
(4)如何查看復雜的JSON數據
方法一:
在線格式化http://tool.oschina.net/codeformat/json
方法二:
把解析后的數據寫plist文件,通過plist文件可以直觀的查看JSON的層次結構。
[dictM writeToFile:@"/Users/cheng/Desktop/videos.plist" atomically:YES];
(5)視頻的簡單播放
//0.需要導入系統框架
#import <MediaPlayer/MediaPlayer.h>
//1.拿到該cell對應的數據字典
XMGVideo *video = self.videos[indexPath.row];
NSString *videoStr = [@"http://120.28.226.186:32812" stringByAppendingPathComponent:video.url];
//2.創建一個視頻播放器
MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:videoStr]];
//3.present播放控制器
[self presentViewController:vc animated:YES completion:nil];
2.0 XML解析
- 2.1 XML簡單介紹
(1) XML:可擴展標記語言
a.語法
b.XML文檔的三部分(聲明、元素和屬性)
c.其它注意點(注意不能交叉包含、空行換行、XML文檔只能有一個根元素等)
(2) XML解析
a.XML解析的兩種方式
001 SAX:從根元素開始,按順序一個元素一個元素的往下解析,可用于解析大、小文件
002 DOM:一次性將整個XML文檔加載到內存中,適合較小的文件
b.解析XML的工具
001 蘋果原生NSXMLParser:使用SAX方式解析,使用簡單
002 第三方框架
libxml2:純C語言的,默認包含在iOS SDK中,同時支持DOM和SAX的方式解析
GDataXML:采用DOM方式解析,該框架由Goole開發,是基于xml2的
- 2.2 XML解析
(1)使用NSXMLParser解析XML步驟和代理方法
//解析步驟:
//4.1 創建一個解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
//4.2 設置代理
parser.delegate = self;
//4.3 開始解析
[parser parse];
-----------------------------------------
//1.開始解析XML文檔
-(void)parserDidStartDocument:(nonnull NSXMLParser *)parser
//2.開始解析XML中某個元素的時候調用,比如<video>
-(void)parser:(nonnull NSXMLParser *)parser didStartElement:(nonnull NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(nonnull NSDictionary<NSString *,NSString *> *)attributeDict
{
if ([elementName isEqualToString:@"videos"]) {
return;
}
//字典轉模型
XMGVideo *video = [XMGVideo objectWithKeyValues:attributeDict];
[self.videos addObject:video];
}
//3.當某個元素解析完成之后調用,比如</video>
-(void)parser:(nonnull NSXMLParser *)parser didEndElement:(nonnull NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName
//4.XML文檔解析結束
-(void)parserDidEndDocument:(nonnull NSXMLParser *)parser
(2)使用GDataParser解析XML的步驟和方法
//4.0 配置環境
// 001 先導入框架,然后按照框架使用注釋配置環境
// 002 GDataXML框架是MRC的,所以還需要告訴編譯器以MRC的方式處理GDataXML的代碼
//4.1 加載XML文檔(使用的是DOM的方式一口氣把整個XML文檔都吞下)
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data options:kNilOptions error:nil];
//4.2 獲取XML文檔的根元素,根據根元素取出XML中的每個子元素
NSArray * elements = [doc.rootElement elementsForName:@"video"];
//4.3 取出每個子元素的屬性并轉換為模型
for (GDataXMLElement *ele in elements) {
XMGVideo *video = [[XMGVideo alloc]init];
video.name = [ele attributeForName:@"name"].stringValue;
video.length = [ele attributeForName:@"length"].stringValue.integerValue;
video.url = [ele attributeForName:@"url"].stringValue;
video.image = [ele attributeForName:@"image"].stringValue;
video.ID = [ele attributeForName:@"id"].stringValue;
//4.4 把轉換好的模型添加到tableView的數據源self.videos數組中
[self.videos addObject:video];
}
- 2.3 多值參數和中文輸出問題
(1)多值參數如何設置請求路徑
//多值參數
/*
如果一個參數對應著多個值,那么直接按照"參數=值&參數=值"的方式拼接
*/
-(void)test
{
//1.確定URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/weather?place=Beijing&place=Guangzhou"];
//2.創建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.發送請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//4.解析
NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
}
(2)如何解決字典和數組中輸出亂碼的問題
答:給字典和數組添加一個分類,重寫descriptionWithLocale方法,在該方法中拼接元素格式化輸出。
-(nonnull NSString *)descriptionWithLocale:(nullable id)locale
3.0 文件下載
- 3.1 小文件下載
(1)第一種方式(NSData)
//使用NSDta直接加載網絡上的url資源(不考慮線程)
-(void)dataDownload
{
//1.確定資源路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];
//2.根據URL加載對應的資源
NSData *data = [NSData dataWithContentsOfURL:url];
//3.轉換并顯示數據
UIImage *image = [UIImage imageWithData:data];
self.imageView.image = image;
}
(2)第二種方式(NSURLConnection-sendAsync)
//使用NSURLConnection發送異步請求下載文件資源
-(void)connectDownload
{
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];
//2.創建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.使用NSURLConnection發送一個異步請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//4.拿到并處理數據
UIImage *image = [UIImage imageWithData:data];
self.imageView.image = image;
}];
}
(3)第三種方式(NSURLConnection-delegate)
//使用NSURLConnection設置代理發送異步請求的方式下載文件
-(void)connectionDelegateDownload
{
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//2.創建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.使用NSURLConnection設置代理并發送異步請求
[NSURLConnection connectionWithRequest:request delegate:self];
}
#pragma mark--NSURLConnectionDataDelegate
//當接收到服務器響應的時候調用,該方法只會調用一次
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//創建一個容器,用來接收服務器返回的數據
self.fileData = [NSMutableData data];
//獲得當前要下載文件的總大小(通過響應頭得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
self.totalLength = res.expectedContentLength;
NSLog(@"%zd",self.totalLength);
//拿到服務器端推薦的文件名稱
self.fileName = res.suggestedFilename;
}
//當接收到服務器返回的數據時會調用
//該方法可能會被調用多次
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// NSLog(@"%s",__func__);
//拼接每次下載的數據
[self.fileData appendData:data];
//計算當前下載進度并刷新UI顯示
self.currentLength = self.fileData.length;
NSLog(@"%f",1.0* self.currentLength/self.totalLength);
self.progressView.progress = 1.0* self.currentLength/self.totalLength;
}
//當網絡請求結束之后調用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//文件下載完畢把接受到的文件數據寫入到沙盒中保存
//1.確定要保存文件的全路徑
//caches文件夾路徑
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName];
//2.寫數據到文件中
[self.fileData writeToFile:fullPath atomically:YES];
NSLog(@"%@",fullPath);
}
//當請求失敗的時候調用該方法
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"%s",__func__);
}
- 3.2 大文件的下載
(1)實現思路
邊接收數據邊寫文件以解決內存越來越大的問題
(2)核心代碼
//當接收到服務器響應的時候調用,該方法只會調用一次
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//0.獲得當前要下載文件的總大?。ㄍㄟ^響應頭得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
self.totalLength = res.expectedContentLength;
NSLog(@"%zd",self.totalLength);
//創建一個新的文件,用來當接收到服務器返回數據的時候往該文件中寫入數據
//1.獲取文件管理者
NSFileManager *manager = [NSFileManager defaultManager];
//2.拼接文件的全路徑
//caches文件夾路徑
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [caches stringByAppendingPathComponent:res.suggestedFilename];
self.fullPath = fullPath;
//3.創建一個空的文件
[manager createFileAtPath:fullPath contents:nil attributes:nil];
}
//當接收到服務器返回的數據時會調用
//該方法可能會被調用多次
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//1.創建一個用來向文件中寫數據的文件句柄
//注意當下載完成之后,該文件句柄需要關閉,調用closeFile方法
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
//2.設置寫數據的位置(追加)
[handle seekToEndOfFile];
//3.寫數據
[handle writeData:data];
//4.計算當前文件的下載進度
self.currentLength += data.length;
NSLog(@"%f",1.0* self.currentLength/self.totalLength);
self.progressView.progress = 1.0* self.currentLength/self.totalLength;
}
- 3.3 大文件斷點下載
(1)實現思路
在下載文件的時候不再是整塊的從頭開始下載,而是看當前文件已經下載到哪個地方,然后從該地方接著往后面下載??梢酝ㄟ^在請求對象中設置請求頭實現。
(2)解決方案(設置請求頭)
//2.創建請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.1 設置下載文件的某一部分
// 只要設置HTTP請求頭的Range屬性, 就可以實現從指定位置開始下載
/*
表示頭500個字節:Range: bytes=0-499
表示第二個500字節:Range: bytes=500-999
表示最后500個字節:Range: bytes=-500
表示500字節以后的范圍:Range: bytes=500-
*/
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
(3)注意點(下載進度并判斷是否需要重新創建文件)
//獲得當前要下載文件的總大?。ㄍㄟ^響應頭得到)
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//注意點:res.expectedContentLength獲得是本次請求要下載的文件的大小(并非是完整的文件的大小)
//因此:文件的總大小 == 本次要下載的文件大小+已經下載的文件的大小
self.totalLength = res.expectedContentLength + self.currentLength;
NSLog(@"----------------------------%zd",self.totalLength);
//0 判斷當前是否已經下載過,如果當前文件已經存在,那么直接返回
if (self.currentLength >0) {
return;
}
- 3.4 輸出流
(1)使用輸出流也可以實現和NSFileHandle相同的功能
(2)如何使用
//1.創建一個數據輸出流
/*
第一個參數:二進制的流數據要寫入到哪里
第二個參數:采用什么樣的方式寫入流數據,如果YES則表示追加,如果是NO則表示覆蓋
*/
NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];
//只要調用了該方法就會往文件中寫數據
//如果文件不存在,那么會自動的創建一個
[stream open];
self.stream = stream;
//2.當接收到數據的時候寫數據
//使用輸出流寫數據
/*
第一個參數:要寫入的二進制數據
第二個參數:要寫入的數據的大小
*/
[self.stream write:data.bytes maxLength:data.length];
//3.當文件下載完畢的時候關閉輸出流
//關閉輸出流
[self.stream close];
self.stream = nil;
- 3.5 使用多線程下載文件思路
01 開啟多條線程,每條線程都只下載文件的一部分(通過設置請求頭中的Range來實現)
02 創建一個和需要下載文件大小一致的文件,判斷當前是那個線程,根據當前的線程來判斷下載的數據應該寫入到文件中的哪個位置。(假設開5條線程來下載10M的文件,那么線程1下載0-2M,線程2下載2-4M一次類推,當接收到服務器返回的數據之后應該先判斷當前線程是哪個線程,假如當前線程是線程2,那么在寫數據的時候就從文件的2M位置開始寫入)
03 代碼相關:使用NSFileHandle這個類的seekToFileOfSet方法,來向文件中特定的位置寫入數據。
04 技術相關
a.每個線程通過設置請求頭下載文件中的某一個部分
b.通過NSFileHandle向文件中的指定位置寫數據
4.0 文件的壓縮和解壓縮
(1)說明
使用ZipArchive來壓縮和解壓縮文件需要添加依賴庫(libz),使用需要包含Main文件,如果使用cocoaPoads來安裝框架,那么會自動的配置框架的使用環境
(2)相關代碼
//壓縮文件的第一種方式
/*
第一個參數:壓縮文件要保存的位置
第二個參數:要壓縮哪幾個文件
*/
[Main createZipFileAtPath:fullpath withFilesAtPaths:arrayM];
//壓縮文件的第二種方式
/*
第一個參數:文件壓縮到哪個地方
第二個參數:要壓縮文件的全路徑
*/
[Main createZipFileAtPath:fullpath withContentsOfDirectory:zipFile];
//如何對壓縮文件進行解壓
/*
第一個參數:要解壓的文件
第二個參數:要解壓到什么地方
*/
[Main unzipFileAtPath:unZipFile toDestination:fullpath];
5.0 文件的上傳
-
5.1 文件上傳步驟
(1)確定請求路徑 (2)根據URL創建一個可變的請求對象 (3)設置請求對象,修改請求方式為POST (4)設置請求頭,告訴服務器我們將要上傳文件(Content-Type) (5)設置請求體(在請求體中按照既定的格式拼接要上傳的文件參數和非文件參數等數據) 001 拼接文件參數 002 拼接非文件參數 003 添加結尾標記 (6)使用NSURLConnection sendAsync發送異步請求上傳文件 (7)解析服務器返回的數據
-
5.2 文件上傳設置請求體的數據格式
//請求體拼接格式 //分隔符:----WebKitFormBoundaryhBDKBUWBHnAgvz9c //01.文件參數拼接格式 --分隔符 Content-Disposition:參數 Content-Type:參數 空行 文件參數 //02.非文件拼接參數 --分隔符 Content-Disposition:參數 空行 非文件的二進制數據 //03.結尾標識 --分隔符--
5.3 文件上傳相關代碼
- (void)upload
{
//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
//2.創建一個可變的請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//3.設置請求方式為POST
request.HTTPMethod = @"POST";
//4.設置請求頭
NSString *filed = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary];
[request setValue:filed forHTTPHeaderField:@"Content-Type"];
//5.設置請求體
NSMutableData *data = [NSMutableData data];
//5.1 文件參數
/*
--分隔符
Content-Disposition:參數
Content-Type:參數
空行
文件參數
*/
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"test.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:KnewLine];
[data appendData:KnewLine];
UIImage *image = [UIImage imageNamed:@"test"];
NSData *imageData = UIImagePNGRepresentation(image);
[data appendData:imageData];
[data appendData:KnewLine];
//5.2 非文件參數
/*
--分隔符
Content-Disposition:參數
空行
非文件參數的二進制數據
*/
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:KnewLine];
[data appendData:KnewLine];
NSData *nameData = [@"wendingding" dataUsingEncoding:NSUTF8StringEncoding];
[data appendData:nameData];
[data appendData:KnewLine];
//5.3 結尾標識
//--分隔符--
[data appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
request.HTTPBody = data;
//6.發送請求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//7.解析服務器返回的數據
NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
}
- 5.4 如何獲得文件的MIMEType類型
(1)直接對該對象發送一個異步網絡請求,在響應頭中通過response.MIMEType拿到文件的MIMEType類型
//如果想要及時拿到該數據,那么可以發送一個同步請求
- (NSString *)getMIMEType
{
NSString *filePath = @"/Users/cheng/Desktop/其它/swift.md";
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]] returningResponse:&response error:nil];
return response.MIMEType;
}
//對該文件發送一個異步請求,拿到文件的MIMEType
- (void)MIMEType
{
// NSString *file = @"file:///Users/cheng/Desktop/test.png";
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:@"/Users/cheng/Desktop/test.png"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
// response.MIMEType
NSLog(@"%@",response.MIMEType);
}];
}
(2)通過UTTypeCopyPreferredTagWithClass方法
//注意:需要依賴于框架MobileCoreServices
- (NSString *)mimeTypeForFileAtPath:(NSString *)path
{
if (![[[NSFileManager alloc] init] fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL);
CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
CFRelease(UTI);
if (!MIMEType) {
return @"application/octet-stream";
}
return (__bridge NSString *)(MIMEType);
}