• iOS开发——下载器的功能基本实现


    今天,做了一个下载器的Demo,即从本地配置的Apache服务器上,下载指定的文件。这次,我们下载服务器根目录下的html.mp4文件。

    按照惯例,我们先创建一个URL对象和请求。

    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/html.mp4"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    这里有两点需要注意,第一,这个url的字符串是全英文的,如果在字符串中出现了中文,我们就不能直接调用URLWithString:这个方法,而是要先将url字符串存入一个字符串对象中,再将这个字符串通过

    [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]

    方法才可以,否则无法正常请求。

    由于是下载操作,我们就需要用到NSURLConnection的代理方法来实现,前提是先创建连接对象和对象的代理。

    //建立连接,立即执行
    [NSURLConnection connectionWithRequest:request delegate:self];

     现在问题来了,代理不止一个可选,<NSURLConnectionDownloadDelegate>和<NSURLConnectionDataDelegate>,初次接触,本能地选择了第一个代理(因为从名字来看,第一个最像)。如果您的想法和我一样,那就错了,第一个代理中的方法实现后,确实可以获得数据,但是不知道数据存在了哪里,并不是我们指定的路径,您可以尝试一下。

    好,经过第一次的失败,我们选择第二个代理,进入头文件,我们看到了四个方法:

    //获得响应
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
    //获取数据
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
    //断开连接
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
    //发生错误
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

    我们可以清楚地了解每个方法的作用,您有兴趣可以打印下每个方法的参数瞧一瞧。

    这里需要补充的是,我们加了几个属性

    ///  文件下载流
    @property (strong, nonatomic) NSOutputStream *fileStream;
    ///  记录文件总长度
    @property (assign, nonatomic) long long fileLength;
    ///  文件当前长度
    @property (assign, nonatomic) long long currentFileLength;

    关于NSOutputStream,还有一个NSFileHandle可以和他进行比较,只是后者会造成文件被重复追加。因此,我们选择前者。根据类名我们可以推断出应该还有一个NSInputStream,没错,一个下载流,一个上传流。

    第一步。在获得响应的方法中,我们从response参数里获得文件的总长度,并且置当前已经下载的文件长度是0,开启一个保存到指定路径的下载流,这里我们保存到桌面。

    //获得响应
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        self.fileLength = response.expectedContentLength;
        //当前文件长度置零
        self.currentFileLength = 0;
        self.fileStream = [[NSOutputStream alloc] initToFileAtPath:@"/Users/xxx/Desktop/html.mp4" append:YES];
        [self.fileStream open];
    }

    第二步。我们获得了数据,如果您在这个方法中打印数据,您就会发现当文件够大(几M就行)时,这个方法会被调用多次,也就是说,分多次获取数据。所以我们在这个方法中拼接数据,同时也要避免数据拼接后造成占用过多内存。我们累加已下载的数据的长度,计算已下载的百分比,并写入数据流中。在计算百分比时,记得转换类型哦,不然结果都是0,除了最后一个是1。

    //获取数据
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
    //    NSLog(@"did receive:%@",data);
        self.currentFileLength += data.length;
        float progressPercent = (float)self.currentFileLength / self.fileLength;
        NSLog(@"have downloaded: %f", progressPercent);
        [self.fileStream write:data.bytes maxLength:data.length];
    }

    最后一步。实际上是两个方法,一个是下载完成调用,一个是下载失败调用。有一点需要注意,无论下载成功或失败,都需要把文件输出流关闭。

    //断开连接
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        NSLog(@"连接结束");
        [self.fileStream close];
    }
    
    //发生错误
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        NSLog(@"%@",error);
        [self.fileStream close];
    }

    这只是下载功能的基本实现,接下来会添加下载进度条,并对下载操作进行跟多优化(多线程,断点续传等),最后会对下载操作进行封装。

    感谢阅读!

  • 相关阅读:
    Python DB API 连接数据库
    PHP base64多图片上传
    Linux vim编写程序时出现高亮字符,如何取消?
    CDN,内容分发网络。
    MySQL随机取数据
    tp5 快速接入扫码支付
    tp5定时器
    清空测试数据
    Centos Crontab查看状态和开启
    select2 使用
  • 原文地址:https://www.cnblogs.com/FrankieZ/p/FrankieZ.html
Copyright © 2020-2023  润新知