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

一 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;
@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];

十 文件压缩/解压缩

+ (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\n
Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n
Content-Type: 文件的MIMEType\r\n
\r\n
文件数据
\r\n
  • 非文件参数
--分割线\r\n
Content-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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值