• OC


    大文件下载注意事项


    • 若不对下载的文件进行转存,会造成内存消耗急剧升高,甚至耗尽内存资源,造成程序终止。
    • 在文件下载过程中通常会出现中途停止的状况,若不做处理,就要重新开始下载,浪费流量。

    大文件下载的解决方案


    • 对下载文件进行处理,每下载一点数据,就将数据写到磁盘中(通常是沙盒中),避免在内存累积数据(NSURLConnection下载)

      • 使用NSFileHandle类实现写数据
      • 使用NSOutputStream类实现写数据
    • 当下载任务终止时,记录任务终止时的位置信息,以便下次开始继续下载

    大文件下载(NSURLConnection)


    • 未支持断点下载
    • 使用NSURLConnection的代理方式下载文件
    • 在下载任务的不同阶段回调的代理方法中,完成转移下载文件,及记录终止位置的任务
    • 使用NSFileHandle类实现写数据的下载步骤(完整核心代码)

      • 设置相关成员属性

        /**所要下载文件的总长度*/
        @property (nonatomic, assign) NSInteger contentLength;
        /**已下载文件的总长度*/
        @property (nonatomic, assign) NSInteger currentLength
        /**文件句柄,用来实现文件存储*/
        @property (nonatomic, strong) NSFileHandle *handle;
      • 创建、发送请求

        // 1. 创建请求路径
        NSURL *url = [NSURL URLWithString:@"此处为URL字符串"];
        // 2. 将URL封装成请求
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        // 3. 通过NSURLConnection,并设置代理
        [NSURLConnection connectionWithRequest:request delegate:self];
      • 遵守代理协议NSURLConnectionDataDelegate,实现代理方法

        /**
        * 接收到服务器响应时调用的方法
        */
        - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
        {
        	//获取所要下载文件的总长度
        	self.contentLength = [response.allHeaderFields[@"Content-Length"] integerValue];
        	//拼接一个沙盒中的文件路径
        	NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"minion_15.mp4"];
        	//创建指定路径的文件
        	[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
        	//创建文件句柄
        	self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
        }
        /**
        * 接收到服务器的数据时调用的方法
        */
        - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
        {
        	//定位到文件尾部,将服务器每次返回的文件数据都拼接到文件尾部
        	[self.handle seekToEndOfFile];
        	//通过文件句柄,将文件写入到沙盒中
        	[self.handle writeData:data];
        	//拼接已下载文件总长度
        	self.currentLength += data.length;
        	//计算下载进度
        	CGFloat progress = 1.0 * self.currentLength / self.contentLength;
        }
        /**
        * 文件下载完毕时调用的方法
        */
        - (void)connectionDidFinishLoading:(NSURLConnection *)connection
        {
        	//关闭文件句柄,并清除
        	[self.handle closeFile];
        	self.handle = nil;
        	//清空已下载文件长度
        	self.currentLength = 0;
        }
    • 使用NSOutputStream类实现写数据的下载步骤(部分代码,其他部分代码同上

      • 设置NSOutputStream成员属性

        @property (nonatomic, strong) NSOutputStream *stream;
      • 初始化NSOutputStream对象,打开输出流

        /**接收到服务器响应的时候调用*/
        - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
        {
        	//获取下载数据保存的路径
        	NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        	NSString *filePath = [cache stringByAppendingPathComponent:response.suggestedFilename];
        	//利用NSOutputStream往filePath文件中写数据,若append参数为yes,则会写到文件尾部
        	self.stream = [[NSOutputStream alloc] initToFileAtPath:filePath append:YES];
        	//打开数据流
        	[self.stream open];
        }
      • 写文件数据

        /**接收到数据的时候调用*/
        - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
        {
        	[self.stream write:[data bytes] maxLength:data.length];
        }
      • 关闭输出流

        /**数据下载完毕的时候调用*/
        - (void)connectionDidFinishLoading:(NSURLConnection *)connection
        {
        	[self.stream close];
        }

    大文件下载(NSURLSession)


    • 支持断点下载,自动记录停止下载时断点的位置
    • 遵守NSURLSessionDownloadDelegate协议
    • 使用NSURLSession下载大文件,被下载文件会被自动写入沙盒的临时文件夹tmp中
    • 下载完毕,通常需要将已下载文件移动其他位置(tmp文件夹中的数据被定时删除),通常是cache文件夹中
    • 详细的下载步骤

      • 设置下载任务task的为成员变量

        @property (nonatomic, strong) NSURLSessionDownloadTask *task;
      • 获取NSURLSession对象

        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
      • 初始化下载任务任务

        self.task = [session downloadTaskWithURL:(此处为下载文件路径URL)];
      • 实现代理方法

        /**每当写入数据到临时文件的时候,就会调用一次该方法,通常在该方法中获取下载进度*/
        - (void)URLSession:(NSURLSession *)session downloadTask: (NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
        {
        	// 计算下载进度
        	CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
        }
        
        /**任务终止时调用的方法,通常用于断点下载*/
        - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
        {
        	//fileOffset:下载任务中止时的偏移量
        }
        
        /**遇到错误的时候调用,error参数只能传递客户端的错误*/
        - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
        { }
        
        /**下载完成的时候调用,需要将文件剪切到可以长期保存的文件夹中*/
        - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
        {
        	//生成文件长期保存的路径
        	NSString *file = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        	//获取文件句柄
        	NSFileManager *fileManager = [NSFileManager defaultManager];
        	//通过文件句柄,将文件剪切到文件长期保存的路径
        	[fileManager moveItemAtURL:location toURL:[NSURL fileURLWithPath:file] error:nil];
        }
      • 操作任务状态

        /**开始/继续下载任务*/
        [self.task resume];
        
        /**暂停下载任务*/
        [self.task suspend];
     
     
  • 相关阅读:
    Android 密匙库导出
    Android常用布局
    asp.net hessian + android hessdroid
    Android文件上传
    android,gridview
    Android文件下载
    Andriod 之数据获取
    java类的泛型DAO
    Spring之AOP编程
    mysql中如何统计某字段里某个字符的个数
  • 原文地址:https://www.cnblogs.com/funny11/p/4945984.html
Copyright © 2020-2023  润新知