iOS 开发-文件下载原理

  • 文件上传
  1. 创建文件上传类FileDownload.h
    //
    //  FileDownload.h
    //  01.文件下载
    //
    //  Created by wyh on 15-1-29.
    //  Copyright (c) 2015年 itcast. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface FileDownload : NSObject
    
    - (void)downloadFileWithURL:(NSURL *)url completion:(void (^)(UIImage *image))completion;
    
    @end

     

  2. 创建文件上传类FileDownload.m
    //
    //  FileDownload.m
    //  01.文件下载
    //
    //  Created by wyh on 15-1-29.
    //  Copyright (c) 2015年 itcast. All rights reserved.
    //
    
    #import "FileDownload.h"
    #import "NSString+Password.h"
    
    #define kTimeOut        2.0f
    // 每次下载的字节数
    #define kBytesPerTimes  20250
    
    @interface FileDownload()
    @property (nonatomic, strong) NSString *cacheFile;
    @property (nonatomic, strong) UIImage *cacheImage;
    @end
    
    @implementation FileDownload
    /**
     为了保证开发的简单,所有方法都不使用多线程,所有的注意力都保持在文件下载上
     
     在开发中如果碰到比较绕的计算问题时,建议:
     1> 测试数据不要太大
     2> 测试数据的数值变化,能够用笔算计算出准确的数值
     3> 编写代码对照测试
    
     */
    //- (NSString *)cacheFile
    //{
    //    if (!_cacheFile) {
    //        NSString *cacheDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
    //        _cacheFile = [cacheDir stringByAppendingPathComponent:@"123.png"];
    //    }
    //    return _cacheFile;
    //}
    - (UIImage *)cacheImage
    {
        if (!_cacheImage) {
            _cacheImage = [UIImage imageWithContentsOfFile:self.cacheFile];
        }
        return _cacheImage;
    }
    
    - (void)setCacheFile:(NSString *)urlStr
    {
        NSString *cacheDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
        urlStr = [urlStr MD5];
        
        _cacheFile = [cacheDir stringByAppendingPathComponent:urlStr];
    }
    
    - (void)downloadFileWithURL:(NSURL *)url completion:(void (^)(UIImage *image))completion
    {
        // GCD中的串行队列异步方法
        dispatch_queue_t q = dispatch_queue_create("cn.itcast.download", DISPATCH_QUEUE_SERIAL);
        
        dispatch_async(q, ^{
            NSLog(@"%@", [NSThread currentThread]);
            
            // 把对URL进行MD5加密之后的结果当成文件名
            self.cacheFile = [url absoluteString];
            
            // 1. 从网络下载文件,需要知道这个文件的大小
            long long fileSize = [self fileSizeWithURL:url];
            // 计算本地缓存文件大小
            long long cacheFileSize = [self localFileSize];
            
            if (cacheFileSize == fileSize) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completion(self.cacheImage);
                });
                NSLog(@"文件已经存在");
                return;
            }
            
            // 2. 确定每个数据包的大小
            long long fromB = 0;
            long long toB = 0;
            // 计算起始和结束的字节数
            while (fileSize > kBytesPerTimes) {
                // 20480 + 20480
                //
                toB = fromB + kBytesPerTimes - 1;
                
                // 3. 分段下载文件
                [self downloadDataWithURL:url fromB:fromB toB:toB];
                
                fileSize -= kBytesPerTimes;
                fromB += kBytesPerTimes;
            }
            [self downloadDataWithURL:url fromB:fromB toB:fromB + fileSize - 1];
    
            dispatch_async(dispatch_get_main_queue(), ^{
                completion(self.cacheImage);
            });        
        });
    }
    
    #pragma mark 下载指定字节范围的数据包
    /**
     NSURLRequestUseProtocolCachePolicy = 0,        // 默认的缓存策略,内存缓存
     
     NSURLRequestReloadIgnoringLocalCacheData = 1,  // 忽略本地的内存缓存
     NSURLRequestReloadIgnoringCacheData
     */
    - (void)downloadDataWithURL:(NSURL *)url fromB:(long long)fromB toB:(long long)toB
    {
        NSLog(@"数据包:%@", [NSThread currentThread]);
        
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:kTimeOut];
        
        // 指定请求中所要GET的字节范围
        NSString *range = [NSString stringWithFormat:@"Bytes=%lld-%lld", fromB, toB];
        [request setValue:range forHTTPHeaderField:@"Range"];
        NSLog(@"%@", range);
        
        NSURLResponse *response = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
        
        // 写入文件,覆盖文件不会追加
    //    [data writeToFile:@"/Users/aplle/Desktop/1.png" atomically:YES];
        [self appendData:data];
        
        NSLog(@"%@", response);
    }
    
    #pragma mark - 读取本地缓存文件大小
    - (long long)localFileSize
    {
        // 读取本地文件信息
        NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:self.cacheFile error:NULL];
        NSLog(@"%lld", [dict[NSFileSize] longLongValue]);
        
        return [dict[NSFileSize] longLongValue];
    }
    
    #pragma mark - 追加数据到文件
    - (void)appendData:(NSData *)data
    {
        // 判断文件是否存在
        NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.cacheFile];
        // 如果文件不存在创建文件
        if (!fp) {
            [data writeToFile:self.cacheFile atomically:YES];
        } else {
            // 如果文件已经存在追加文件
            // 1> 移动到文件末尾
            [fp seekToEndOfFile];
            // 2> 追加数据
            [fp writeData:data];
            // 3> 写入文件
            [fp closeFile];
        }
    }
    
    #pragma mark - 获取网络文件大小
    - (long long)fileSizeWithURL:(NSURL *)url
    {
        // 默认是GET
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:kTimeOut];
        
        // HEAD 头,只是返回文件资源的信息,不返回具体是数据
        // 如果要获取资源的MIMEType,也必须用HEAD,否则,数据会被重复下载两次
        request.HTTPMethod = @"HEAD";
    
        // 使用同步方法获取文件大小
        NSURLResponse *response = nil;
        
        [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
        
        // expectedContentLength文件在网络上的大小
        NSLog(@"%lld", response.expectedContentLength);
        
        return response.expectedContentLength;
    }
    
    @end

     

  3. 控制器中调用
    //
    //  ViewController.m
    //  01.文件下载
    //
    //  Created by wyh on 15-1-29.
    //  Copyright (c) 2015年 itcast. All rights reserved.
    //
    
    #import "ViewController.h"
    #warning 包含FileDownload.h文件
    #import "FileDownload.h"
    
    @interface ViewController ()
    @property (nonatomic, strong) FileDownload *download;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    #warning 创建FileDownload对象,并调用方法downloadFileWithURL:
        self.download = [[FileDownload alloc] init];
        [self.download downloadFileWithURL:[NSURL URLWithString:@"http://localhost/itcast/images/head4.png"] completion:^(UIImage *image) {
            
            self.imageView.image = image;
        }];
    }
    
    @end

     

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。