美文网首页v2panda的技术专题iOS奇思怪巧iOS-Vendor
iOS开发网络篇之文件下载、大文件下载、断点下载

iOS开发网络篇之文件下载、大文件下载、断点下载

作者: 勤奋的笨老头 | 来源:发表于2015-09-11 00:29 被阅读41741次
    这里写图片描述这里写图片描述

    iOS开发中经常会用到文件的下载与上传功能,今天咱们来分享一下文件下载的思路。文件上传下篇再说。

    文件下载分为小文件下载与大文件下载

    小文件下载

    小文件可以是一张图片,或者一个文件,这里指在现行的网络状况下基本上不需要等待很久就能下载好的文件。这里以picjumbo里的一张图片为例子。

    NSData方式

    其实我们经常用的[NSData dataWithContentsOfURL] 就是一种文件下载方式,猜测这里面应该是发送了Get请求。

    NSURL *url = [NSURL URLWithString:@"https://picjumbo.imgix.net/HNCK8461.jpg?q=40&w=1650&sharp=30"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    

    当然下载代码应该放到子线程执行

    NSURLConnection方式下载

    NSURL* url = [NSURL URLWithString:@"https://picjumbo.imgix.net/HNCK8461.jpg?q=40&w=1650&sharp=30"];
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
            
    self.imageView.image = [UIImage imageWithData:data];
        }];
    

    就是发送一个异步的Get请求,回调的data就是我们下载到的图片。
    这些都很简单,今天主要说的是大文件的下载。

    大文件下载

    NSURLConnection下载

    通过上面的两个方法去下载大文件是不合理的,因为这两个方法都是一次性返回整个下载到的文件,返回的data在内存中,如果下载一个几百兆的东西,内存肯定会爆的。
    其实NSURLConnection还提供了另外一种发送请求的方式

    // 发送请求去下载 (创建完conn对象后,会自动发起一个异步请求)
    [NSURLConnection connectionWithRequest:request delegate:self];
    

    这里用到了代理,那肯定要遵守协议了.遵守NSURLConnectionDataDelegate 协议.
    进去看看有几个代理方法,其实我们能用到的也就三个。

    /**
     *  请求失败时调用(请求超时、网络异常)
     *
     *  @param error      错误原因
     */
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        
    }
    /**
     *  1.接收到服务器的响应就会调用
     *
     *  @param response   响应
     */
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        
    }
    /**
     *  2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
     *
     *  @param data       这次返回的数据
     */
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        
    }
    /**
     *  3.加载完毕后调用(服务器的数据已经完全返回后)
     */
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        
    }
    

    通过执行下载操作,分别log上面三个方法,会发现didReceiveData这个方法会被频繁的调用,每次都会传回来一部分data,下面是官方api对这个方法的说明

    is called with a single immutable NSData object to the delegate,
    representing the next portion of the data loaded from the connection. This is the only guaranteed for the delegate to receive the data from the resource load.

    由此我们可以知道,这种下载方式是通过这个代理方法每次传回来一部分文件,最终我们把每次传回来的数据合并成一个我们需要的文件。

    这时候我们通常想到的方法是定义一个全局的NSMutableData,接受到响应的时候初始化这个MutableData,在didReceiveData方法里面去拼接
    [self.totalData appendData:data];
    最后在完成下载的方法里面吧整个MutableData写入沙盒。
    代码如下:

    @property (weak, nonatomic) IBOutlet UIProgressView *myPregress;
    
    @property (nonatomic,strong) NSMutableData* fileData;
    
    /**
     *  文件的总长度
     */
    @property (nonatomic, assign) long long totalLength;
    
    /**
     *  1.接收到服务器的响应就会调用
     *
     *  @param response   响应
     */
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        self.fileData = [NSMutableData data];
        // 获取要下载的文件的大小
        self.totalLength = response.expectedContentLength;
    }
    /**
     *  2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
     *
     *  @param data       这次返回的数据
     */
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        [self.fileData appendData:data];
        self.myPregress.progress = (double)self.fileData.length / self.totalLength;
    }
    /**
     *  3.加载完毕后调用(服务器的数据已经完全返回后)
     */
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        // 拼接文件路径
        NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString *file = [cache stringByAppendingPathComponent:response.suggestedFilename];
        
        // 写到沙盒中
        [self.fileData writeToFile:file atomically:YES];
    }
    

    我这里下载的是javajdk。(度娘的地址)
    注意:通常大文件下载是需要给用户展示下载进度的。
    这个数值是 已经下载的数据大小/要下载的文件总大小
    已经下载的数据我们可以记录,要下载的文件总大小在服务器返回的响应头里面可以拿到,在接受到响应的方法里执行

     NSHTTPURLResponse *res = (NSHTTPURLResponse*)response;
        
        NSDictionary *headerDic = res.allHeaderFields;
        NSLog(@"%@",headerDic);
        self.fileLength = [[headerDic objectForKey:@"Content-Length"] intValue];
    

    不得不说苹果太为开发者考虑了,我们不必这么麻烦的去获取文件总大小了,
    response.expectedContentLength 这句代码就搞定了。
    response.suggestedFilename 这句代表获取下载的文件名


    这里写图片描述这里写图片描述

    题外话扯的有点多,言归正传,这样我们确实可以下载文件,最后拿到的文件也能正常运行


    这里写图片描述这里写图片描述

    但是有个致命的问题,内存!用来接受文件的NSMutableData一直都在内存中,会随着文件的下载一直变大,


    这里写图片描述这里写图片描述

    所有这种处理方式绝对是不合理的。

    合理的方式在我们获取一部分data的时候就写入沙盒中,然后释放内存中的data。

    这里要用到NSFilehandle这个类,这个类可以实现对文件的读取、写入、更新。
    下面总结了一些常用的NSFileHandle的方法,在这个表中,fh是一个NSFileHandle对象,data是一个NSData对象,path是一个NSString 对象,offset是易额Unsigned long long变量。


    这里写图片描述这里写图片描述

    具体关于NSFileHandle的用法各位自行搜索。

    在接受到响应的时候就在沙盒中创建一个空的文件,然后每次接收到数据的时候就拼接到这个文件的最后面,通过- (unsigned long long)seekToEndOfFile; 这个方法

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        // 文件路径
        NSString* ceches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString* filepath = [ceches stringByAppendingPathComponent:response.suggestedFilename];
        
        // 创建一个空的文件到沙盒中
        NSFileManager* mgr = [NSFileManager defaultManager];
        [mgr createFileAtPath:filepath contents:nil attributes:nil];
        
        // 创建一个用来写数据的文件句柄对象
        self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filepath];
        
        // 获得文件的总大小
        self.totalLength = response.expectedContentLength;
                            
    }
    /**
     *  2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次)
     *
     *  @param data       这次返回的数据
     */
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        // 移动到文件的最后面
        [self.writeHandle seekToEndOfFile];
        
        // 将数据写入沙盒
        [self.writeHandle writeData:data];
        
        // 累计写入文件的长度
        self.currentLength += data.length;
        
        // 下载进度
        self.myPregress.progress = (double)self.currentLength / self.totalLength;
    }
    /**
     *  3.加载完毕后调用(服务器的数据已经完全返回后)
     */
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        self.currentLength = 0;
        self.totalLength = 0;
        
        // 关闭文件
        [self.writeHandle closeFile];
        self.writeHandle = nil;
    }
    

    这样在下载过程中内存就会一直很稳定了,并且下载的文件也是没问题的。


    这里写图片描述这里写图片描述
    断点下载

    暂停/继续下载也是现在下载中必备的功能了,如果没有暂停功能,用户体验相比会很差,而且如果突然网络不好中断了,没有实现断点下载的话只有重新下了。。。
    下面让我们来加入断点下载功能吧。
    NSURLConnection 只提供了一个cancel方法,这并不是暂停,而是取消下载任务。如果要实现断点下载必须要了解HTTP协议中请求头的Range。

    这里写图片描述这里写图片描述

    不难看出,通过设置请求头的Range我们可以指定下载的位置、大小。
    那么我们这样设置bytes=500- 从500字节以后的所有字节,
    只需要在didReceiveData中记录已经写入沙盒中文件的大小(self.currentLength),
    把这个大小设置到请求头中,因为第一次下载肯定是没有执行过didReceive方法,self.currentLength也就为0,也就是从头开始下。

    注意:通过这种方法,我从我们公司的服务器上下载东西暂停开始都没用问题,下载好的视频也能正常播放,但是百度软件的下载链接不成功(暂停以后在请求服务器不给响应),应该是百度服务器做了限制吧,或者我写的不完美,有想法欢迎交流。

    上代码:

    pragma mark --按钮点击事件

    • (IBAction)btnClicked:(UIButton *)sender {

      // 状态取反
      sender.selected = !sender.isSelected;

      // 断点续传
      // 断点下载

      if (sender.selected) { // 继续(开始)下载
      // 1.URL
      NSURL *url = [NSURL URLWithString:@"http://localhost:8080//term_app/hdgg.zip"];

        // 2.请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        
        // 设置请求头
        NSString *range = [NSString stringWithFormat:@"bytes=%lld-", self.currentLength];
        [request setValue:range forHTTPHeaderField:@"Range"];
        
        // 3.下载
        self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
      

      } else { // 暂停

        [self.connection cancel];
        self.connection = nil;
      

      }
      }

    
    在下载过程中,为了提高效率,充分利用cpu性能,通常会执行多线程下载,代码就不贴了,分析一下思路:
    
    > 下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分)。
    
    ####NSURLSession下载方式
    上面这种下载文件的方式确实比较复杂,要自己去控制内存写入相应的位置,不过在苹果在iOS7推出了一个新的类```NSURLSession```,它具备了NSURLConnection所具备的方法,同时也比它更强大。苹果推出它的目的大有取代NSURLConnection的趋势或者目的。
    
    ```NSURLSession``` 也可以发送Get/Post请求,实现文件的下载和上传。
    在NSURLSesiion中,任何请求都可以被看做是一个任务。其中有三种任务类型
    
    > // NSURLSessionDataTask : 普通的GET\POST请求
    // NSURLSessionDownloadTask : 文件下载
    // NSURLSessionUploadTask : 文件上传(很少用,一般服务器不支持)
    
    #####NSURLSession 简单使用
    NSURLSession发送请求非常简单,与connection不同的是,任务创建后不会自动发送请求,需要手动开始执行任务。
    
    

    // 1.得到session对象
    NSURLSession* session = [NSURLSession sharedSession];
    NSURL* url = [NSURL URLWithString:@""];

    // 2.创建一个task,任务
    NSURLSessionDataTask* dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        // data 为返回数据
    }];
    
    // 3.开始任务
    [dataTask resume];
    
    
    

    // 发送post请求 自定义请求头
    [session dataTaskWithRequest:<#(NSURLRequest *)#> completionHandler:<#^(NSData *data, NSURLResponse *response, NSError *error)completionHandler#>]

    
     
    
    #####NSURLSession 下载
    使用NSURLSession就非常简单了,不需要去考虑什么边下载边写入沙盒的问题,苹果都帮我们做好了。代码如下
    
    

    NSURL* url = [NSURL URLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"];

    // 得到session对象
    NSURLSession* session = [NSURLSession sharedSession];
    
    // 创建任务
    NSURLSessionDownloadTask* downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        
    }];
    // 开始任务
    [downloadTask resume];
    
    是不是跟NSURLConnection很像,但仔细看会发现回调的方法里面并没用NSData传回来,多了一个location,顾名思义,location就是下载好的文件写入沙盒的地址,打印一下发现下载好的文件被自动写入的temp文件夹下面了。
    
    location:file:///Users/yeaodong/Library/Developer/CoreSimulator/Devices/E52B4B95-53E1-46A2-9881-8C969958FBC0/data/Containers/Data/Application/BFB9F0CA-0F50-4682-BBBD-B71B54C39EBE/tmp/CFNetworkDownload_YNnuIS.tmp
    
    ![这里写图片描述](http://img.blog.csdn.net/20150910155745812)
    
    不过在下载完成之后会自动删除temp中的文件,所有我们需要做的只是在回调中把文件移动(或者复制,反正之后会自动删除)到caches中。
    
    

    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
    NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];

        // 将临时文件剪切或者复制Caches文件夹
        NSFileManager *mgr = [NSFileManager defaultManager];
        
        // AtPath : 剪切前的文件路径
        // ToPath : 剪切后的文件路径
        [mgr moveItemAtPath:location.path toPath:file error:nil];
    
    
    不过通过这种方式下载有个缺点就是无法监听下载进度,要监听下载进度,苹果通常的作法是通过delegate,这里也一样。而且NSURLSession的创建方式也有所不同。
    首先遵守协议``` <NSURLSessionDownloadDelegate>``` 注意不要写错
    点进去发现协议里面有三个方法。
    
    

    pragma mark -- NSURLSessionDownloadDelegate

    /**

    • 下载完毕会调用
    • @param location 文件临时地址
      */
    • (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask )downloadTask
      didFinishDownloadingToURL:(NSURL )location
      {
      }
      /
    • 每次写入沙盒完毕调用
    • 在这里面监听下载进度,totalBytesWritten/totalBytesExpectedToWrite
    • @param bytesWritten 这次写入的大小
    • @param totalBytesWritten 已经写入沙盒的大小
    • @param totalBytesExpectedToWrite 文件总大小
      */
    • (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
      totalBytesWritten:(int64_t)totalBytesWritten
      totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
      {
      self.pgLabel.text = [NSString stringWithFormat:@"下载进度:%f",(double)totalBytesWritten/totalBytesExpectedToWrite];
      }

    /**

    • 恢复下载后调用,
      */
    • (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didResumeAtOffset:(int64_t)fileOffset
      expectedTotalBytes:(int64_t)expectedTotalBytes
      {

    }

    
    这上面的注释已经很详细了,相信大家都能看懂吧。
    
    NSURLSession创建方式,这里就不能使用Block回调方式了,如果给下载任务设置了completionHandler这个block,也实现了下载的代理方法,优先执行block,代理方法也就不会执行了。
    
    

    // 得到session对象
    NSURLSessionConfiguration* cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; // 默认配置

    NSURLSession* session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 创建任务
    NSURLSessionDownloadTask* downloadTask = [session downloadTaskWithURL:url];
    
    // 开始任务
    [downloadTask resume];
    
    
    相比之前的NSURLConnection方式简单很多吧,用NSURLSessionDownloadTask做断点下载也很简单,我们先了解一下任务的取消方法
    
    
    • (void)cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler;
    取消操作以后会调用一个Block,并传入一个resumeData,该参数包含了继续下载文件的位置信息。也就是说,当你下载了10M得文件数据,暂停了。那么你下次继续下载的时候是从第10M这个位置开始的,而不是从文件最开始的位置开始下载。因而为了保存这些信息,所以才定义了这个NSData类型的这个属性:resumeData。这个data只包含了url跟已经下载了多少数据,不会很大,不用担心内存问题。
    
    另外,session还提供了通过resumeData来创建任务的方法
    
    
    • (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
    我们只需要在取消操作的回调中记录好resumeData,然后在恢复下载的适合通过上面的方法创建任务就好了,相比NSURLconnection简单太多了。
    需要注意的是Block中循环引用的问题
    
    

    __weak typeof(self) selfVc = self;
    [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
    selfVc.resumeData = resumeData;
    selfVc.downloadTask = nil;
    }];

    示例程序下载:https://github.com/hongfenglt/HFDownLoad
    
    这篇博客断断续续写了两三天,可能某些地方思路有些乱,欢迎大神指正。

    相关文章

      网友评论

      • wanglei1702:你好,文章中的插图不显示?是网络图片吗,加载不出来。
      • LD_左岸://下载任务

        __block NSURLSessionDownloadTask * task = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        NSURL * pathUrl = [NSURL fileURLWithPath:fileSavePath];
        return pathUrl;
        } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        if (error) {
        [self showError:error];
        }

        }];
        [task resume];



        我用的AFN下载 下载完我发现Temp文件夹里有很多tmp结尾的文件如CFNetworkDownload_AWy6Qg.tmp等等 这些文件是一直递增的 导致app占用手机空间持续增大...........楼主知道咋回事吗
      • 疯狂小跳虫:相当实用 谢谢大神分享
      • 1条大菜狗:现在都用NSURLSession了,这个应该被废弃了~
      • JoinPerson:貌似不能实现下载在进行的过程中 Kill 应用之后的断点续传。
      • nzbypl:期待大文件上传楼主。大神 给力
      • ba172557b62b:你好!怎么实时获取当前的网速呢??
      • Kevin_Ray:为什么下载不动啊?
      • 5ebffcf2f153:有个问题 在NSURLSession下载方式里面 用了NSURLSessionDownloadDelegate 里面的代理方法去监听下载进度以及下载完毕之后的文件移动处理,那么没有手动去暂停和继续下载的情况下,下载一半 突然断网了,这个时候 有什么办法得到这个下载一半的失败?这个时候并没有下载结束,那个下载结束的代理方法应该也是不会走的吧,
      • 一只霸天犬v:楼主 要是批量下载怎么做啊


      • JakerMan:请问使用NSURLSession可以通过一台windows 给app传文件吧.从而实现两台设备之间的文件传输.可以吗?
      • 雪_晟:应用杀死后并没有继续原来的进度下载啊
      • mamat:afnetworking 用的就是NSURLSessionDownloadTask
      • mamat:兄弟 一整篇看完了 .. 想看 程序关了之后 再进来的断点下载 不是下载一般 退了进来又是重新下载,这样下载直接用afnetworking 下载不是更直接吗
      • 戏子缠绵:感谢楼主分享,希望继续分享,快回来吧,快回来入坑。
      • d5e6721efe12:感谢分享,好人一生平安,阿门!
      • 低调的魅力:不错哦,楼主写的不错,加油
      • 哈哈哈我的简书账号:您好,问一下,如果调- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData的时候,tmp文件被删除啦,但是resumeData文件还在,怎么办?自己做实验试过,这时候任务调用resume方法,任务的状态不确定,有可能立即结束,有可能卡在运行状态,就是state ==0
      • 7d8ce36dc879:博主有没有尝试过既能断点续传又能后台下载,最近被这个搞得头都大了,因为断点续传,获取不到下载到一半时,程序退出,文件的路径
      • ca5a70300819:楼主断点下载写的麻烦了,苹果直接提供了- (void)suspend;
        - (void)resume;
        楼主用的那种写法应该是暂停退出应用,然后回到应用后还难继续下载。才用- (void)cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler;
        但是如果是退出应用然后再回回到应用后还能继续下载的话,楼主的写法也不能实现这种功能。这是我自己的拙见。望和楼主讨论
      • doubleJJ:请问一下,在使用NSURLSession下载的时候,用NSURLSessionDownloadDelegate的方法来监听下载进度,没有触发那个代理方法,是什么原因呢,didFinishDownloadingToURL这个代理方法是有触发的,就是监听进度的那个方法没有执行,百思不得其解啊,求好心人帮助!!
      • 一杯开水:LZ大大,我是做IOS的,我遇到一个问题:在用户登录的时候APP会检测本地数据库是否需要更新,如果需要就去服务器下载,下载的是.db的数据库文件,我该怎么在工程中进行存取数据库?我现在是存到沙盒目录下,但是不怎么到该怎么取出来使用.求LZ大大帮忙 :sob:
      • 40c6620494c9:为什么不能直接用data writeto 方法, 而要创建nsfilehandle,这两个有什么区别吗。??
      • SuperMan_Wang:哎呦,不错哦!这个比较 叼! :arrow_up:
      • 响尾猫:看了下楼主写的伪断点续传,很清晰,如果想完善的话,希望加入真的断点续传,
        1、重复进入页面进度加载。(这里是重新加载)
        2、下载已经下载过的文件,应该直接从内存加载。
        3、下载过一半的文件,程序退出,再进来可以继续下载。
        4、文件批量下载,多线程下载等。希望楼主做的更好。
        1江春水:这些需要自己来完成:smiley:
        新地球说着一口陌生腔调:他没有使用 downloadTaskWithResumeData 吗?
      • dd8a1077569c:文章没有很乱 写的很清楚 因为毕竟大家都是用过这些方法的 介绍的也很详细 我去下载代码看看 主要是最近要做一个 断点下载的东西 但是 AF 并没有支持 所以想自己写
      • 黑幕居士:循序渐进,太好懂了!
      • zyg:期待 文件上传
      • 257f312dccb8:貌似不支持程序突然中断这种情况的断点下载。。
      • qBryant:分析的很透彻,同时分析对比了内存占用,学习了 :+1: 。。。后期会有AFN的么? :smile:
      • 8e44f820f2ad:学习了
      • 司马捷:使用NSURLSesstion 断点续传的时候,如何将文件时时保存在本地?
      • daixunry:session的方式是不是,如果暂停了下载,那么tmp下的该文件不会被删除,resume接着继续下载,会自动往这个文件写入?
        因为没有看到需要手动合并文件的地方
      • c18456007e32:您好,请问断点上传这块有没有推荐的资料。目前我自己弄的上传大文件一直没弄好
      • ce5cbd3b65d0:写的很棒,很期待你更新AFNetWorking的断点下载。 :blush:
      • QingQueen:先收藏了哈哈
      • 其实也是:写的很好,爱你
        勤奋的笨老头:@其实也是 头像是red?我也很喜欢这部美剧
      • 9a6940db9030:受教了
      • 木菀:非常感谢,正好需要用到
      • b134614228c3:加油,看好你的,💖💖💖💖💖💖💖
      • Wow_我了个去:精品哈。加油↖(^ω^)↗
      • 7eb87033b5d6:支持,通俗易懂。
      • 不是谢志伟:不错,谢谢分享
      • darnel:感谢分享。 :+1:
      • 王大先森:求上传咯。然后作者如果能同时剖析一些著名第三方库实现下载的原理的话,那就更棒咯。总之非常感谢
        勤奋的笨老头: @王大先森 那到没有,后续应该会写结合AFN的
        王大先森:@勤奋的笨老头 是结合其他第三方库的咯?
        勤奋的笨老头: @王大先森 文章最下面不是有demo下载嘛?
      • 永战:非常感谢,求上传,求 :smiley: 剧透一下,上传有关于错误和重试的部分吗?
      • David_Cap:这个有点 屌。
      • SeanKChan:感谢楼主分享
      • a7642f69975b:刚好需要
      • 49c9990ff26c:期待 文件上传

      本文标题:iOS开发网络篇之文件下载、大文件下载、断点下载

      本文链接:https://www.haomeiwen.com/subject/abypcttx.html