ios开发进阶之网络04 数据解析 文件下载上传

来源:互联网 发布:淘宝全球购假货曝光 编辑:程序博客网 时间:2024/06/04 00:24

一 JSON

  • 什么是JSON
    • JSON是一种轻量级的数据格式,一般用于数据交互
    • 服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外)
  • JSON和OC对象转换后对应数据类型
 {} -> NSDictionary @{} [] -> NSArray @[] "jack" -> NSString @"jack" 10 -> NSNumber @10 10.5 -> NSNumber @10.5 true -> NSNumber @1 false -> NSNumber @0 null -> NSNull
  • JSON解析方案
    • 第三方框架:JSONKit、SBJson、TouchJSON(性能从左到右,越差)
    • 苹果原生(自带):NSJSONSerialization(性能最好)
  • NSJSONSerialization的常见方法
// JSON数据 → OC对象+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
  • NSJSONReadingOptions
    • NSJSONReadingMutableContainers = (1UL << 0)
      • 创建出来的数组和字典就是可变
    • NSJSONReadingMutableLeaves = (1UL << 1)
      • 数组或者字典里面的字符串是可变的, iOS7以后无效
    • NSJSONReadingAllowFragments
      • 允许解析出来的对象不是字典或者数组,比如直接是字符串或者NSNumber
// OC对象 → JSON数据 + (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
  • 格式化服务器返回的JSON数据
    • 在线格式化:http://tool.oschina.net/codeformat/json
    • 将服务器返回的字典或者数组写成plist文件
  • 复杂JSON数据解析案例代码
@interface ViewController ()@property (nonatomic, strong) NSArray *videos; /**< 视屏信息 */@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    self.tableView.rowHeight = 150;    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/video?type=JSON"];    NSURLRequest *request = [NSURLRequest requestWithURL:url];    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];        self.videos = dict[@"videos"];        // 注意: 拿到数据之后一定要刷新表格        [self.tableView reloadData];    }]; }#pragma mark - datasource- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return self.videos.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    // 1.创建cell    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];    // 2.取出对应行的字典    NSDictionary *dict = self.videos[indexPath.row];    // 2.1设置数据    cell.textLabel.text = dict[@"name"];    cell.detailTextLabel.text = [NSString stringWithFormat:@"时长:%@", dict[@"length"]];    NSString *urlStr = [NSString stringWithFormat:@"http://120.25.226.186:32812/%@", dict[@"image"]];    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:urlStr] placeholderImage:nil];    // 3.返回cell    return cell;}// 监听点击- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    // 1.取出选中行对应的字典    NSDictionary *dict = self.videos[indexPath.row];    // 2.根据字典中的URL属性拼接视屏的地址    NSString *urlStr = [NSString stringWithFormat:@"http://120.25.226.186:32812/%@", dict[@"url"]];    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];    NSURL *url = [NSURL URLWithString:urlStr];    // 3.播放视屏    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:url];    // 4.显示控制器    [self presentViewController:vc animated:YES completion:nil];}@end

二 字典转模型框架

  • Mantle
    • 所有模型都必须继承自MTModel
  • JSONModel
    • 所有模型都必须继承自JSONModel
  • MJExtension
    • 不需要强制继承任何其他类

设计框架需要考虑的问题

  • 侵入性
    • 侵入性大就意味着很难离开这个框架
  • 易用性
    • 比如少量代码实现N多功能
  • 扩展性
    • 很容易给这个框架增加新框架

利用苹果官方API播放视频

// 创建视频播放器MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:urlStr]];// 显示视频[self presentViewController:vc animated:YES completion:nil];

三 XML

  • 什么是XML
    • 全称是Extensible Markup Language,译作“可扩展标记语言”
    • 跟JSON一样,也是常用的一种用于交互的数据格式
    • 一般也叫XML文档(XML Document)
  • XML语法

    • 文档声明

      • 声明XML文档的类型
    • 元素(Element)

      • 一个元素包括了开始标签和结束标签
    • 属性(Attribute)

      • 一个元素可以拥有多个属性
  • XML的解析方式
    • SAX
      • 大小文件都可以
      • NSXMLParser
    • DOM
      • 最好是小文件
      • GDataXML

四 XML-SAX解析 NSXMLParser

  • 创建解析器来解析
// 创建XML解析器NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];// 设置代理parser.delegate = self;// 开始解析XML(parse方法是阻塞式的)[parser parse];
  • 代理对象要遵守NSXMLParserDelegate协议,实现代理方法
/** * 解析到某个元素的结尾(比如解析</videos>) */- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{}/** * 解析到某个元素的开头(比如解析<videos>) */- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{}/** * 开始解析XML文档 */- (void)parserDidStartDocument:(NSXMLParser *)parser{}/** * 解析完毕 */- (void)parserDidEndDocument:(NSXMLParser *)parser{}

五 XML-DOM解析

  • GDataXML基于libxml2库,得做以下配置:

这里写图片描述

这里写图片描述

  • GDataXML是非ARC的,因此得设置编译参数

这里写图片描述

  • 具体用法
// 加载整个文档GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];// 获得根节点doc.rootElement;// 获得其他节点[element elementsForName:@"video"];// 获得节点的属性[element attributeForName:@"name"].stringValue;

六 小文件下载

  • 方法一
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_15.png"];    NSData *data = [NSData dataWithContentsOfURL:url];
  • 方法二
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_15.png"];    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {    }];

七 大文件下载

  • 如果是大文件下载,建议使用NSURLSession或者第三方框架
- (void)viewDidLoad {    [super viewDidLoad];    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_15.mp4"];    [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self];}#pragma mark - NSURLConnectionDataDelegate- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response{    // 判断如果有数据, 就不需要重新创建    if (self.currentLength) return;    // 获取文件总大小//    self.contentLength = [response.allHeaderFields[@"Content-Length"] integerValue];    self.contentLength = response.expectedContentLength;    NSLog(@"%@", XMGFile);    // 创建一个空的文件    [[NSFileManager defaultManager] createFileAtPath:XMGFile contents:nil attributes:nil];    // 创建文件句柄    self.handle = [NSFileHandle fileHandleForWritingAtPath:XMGFile];}// 不将文件保存到内存中,一边下载一边写入磁盘中,防止内存暴增- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{    // 指定数据的写入位置 -- 文件内容的最后面    [self.handle seekToEndOfFile];    // 写入数据    [self.handle writeData:data];     // 拼接总长度    self.currentLength += data.length;    self.progressView.progress = 1.0 * self.currentLength / self.contentLength;    NSLog(@"%f", self.progressView.progress);}// 下载完毕一定要关闭文件句柄- (void)connectionDidFinishLoading:(NSURLConnection *)connection{    // 关闭handle    [self.handle closeFile];    self.handle = nil;    // 清空长度    self.currentLength = 0;    self.contentLength = 0;}- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{}

八 输出流

  • 用NSOutputStream可以将网络请求的资源回来的数据保存到本地文件
- (NSOutputStream *)outputStream{    if (!_outputStream) {        /*         第一个参数: 告诉系统数据流需要输出到哪个文件中         第二个参数: 如果传入YES, 代表每次都在上一次的后面追加         如果传入NO, 代表每次都从头开始         */        _outputStream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES];        // 注意: 如果想利用输出流写入数据, 一定要打开数据流        // 如果数据流打开的文件不存在, 那么会自动创建个新的        [_outputStream open];    }    return _outputStream;}
// 接收到服务器的数据调用(会调用一次或多次)- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{   // 直接写入数据    /*     第一个参数: 需要写入的数据     第二个参数: 写入数据的大小     */    [self.outputStream write:data.bytes maxLength:data.length];    // 计算进度    self.currentLength += data.length;    self.progressView.progress = 1.0 * self.currentLength / self.totalLength;}// 接收完毕时调用- (void)connectionDidFinishLoading:(NSURLConnection *)connection{    // 关闭输出流    [self.outputStream close];    self.outputStream = nil;}

九 文件断点下载

NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];    NSURLRequest *request = [NSURLRequest requestWithURL:url];    // 拿到当前已经下载文件的大小    NSString *path = [@"minion_01.mp4" cacheDir];    NSFileManager *manager = [NSFileManager defaultManager];    NSDictionary *dict = [manager attributesOfItemAtPath:path error:nil];    //设置请求头    /*     表示头500个字节:bytes= 0-499     表示第二个500个字节:bytes= 500-999     表示最后500个字节:bytes= -500     表示500字节以后的范围:bytes= 500-     第一个和最后一个字节:bytes=0-0,-1     同时指定几个范围:bytes= 300~499,500-899     */    // 注意:从文件中获取出来的大小一定要先转换为integerVa,否则是null    NSString *range = [NSString stringWithFormat:@"bytes:%zd-",[dict[NSFileSize] integerValue]];    [request setValue:range forHTTPHeaderField:@"Range"];    [NSURLConnection connectionWithRequest:request delegate:self];

十 文件压缩/解压缩

  • ZipArchive
    • 下载地址:https://github.com/ZipArchive/ZipArchive
    • 需要引入libz.dylib框架
  • 压缩
+ (BOOL)createZipFileAtPath:(NSString *)path           withFilesAtPaths:(NSArray *)paths;+ (BOOL)createZipFileAtPath:(NSString *)path    withContentsOfDirectory:(NSString *)directoryPath;
  • 解压缩
+ (BOOL)unzipFileAtPath:(NSString *)path          toDestination:(NSString *)destination

十一 文件上传

[request setValue:@"multipart/form-data; boundary=分割线" forHTTPHeaderField:@"Content-Type"];

设置请求体

  • 文件参数
--分割线\r\nContent-Disposition: form-data; name="参数名"; filename="文件名"\r\nContent-Type: 文件的MIMEType\r\n\r\n文件数据\r\n
  • 非文件参数
--分割线\r\nContent-Disposition: form-data; name="参数名"\r\n\r\n参数值\r\n
  • 结束标记
参数结束的标记--分割线--\r\n
  • 注意事项

    • 请求体比请求头分割线前面多两个--
    • 结束标记比请求体后面多两个--

    十二 MIMEType

  • 利用NSURLConnection

    • 万能类型 application/octet-
  • 部分文件的MIMEType

这里写图片描述

- (NSString *)MIMEType:(NSURL *)url{    // 1.创建一个请求    NSURLRequest *request = [NSURLRequest requestWithURL:url];    // 2.发送请求(返回响应)    NSURLResponse *response = nil;    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];    // 3.获得MIMEType    return response.MIMEType;}
  • C语言API(了解)
+ (NSString *)mimeTypeForFileAtPath:(NSString *)path{    if (![[NSFileManager alloc] init] fileExistsAtPath:path]) {        return nil;    }    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL);    CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);    CFRelease(UTI);    if (!MIMEType) {        return @"application/octet-stream";    }    return NSMakeCollectable(MIMEType);}
1 0