zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    instruments 内存泄漏
    ios常用数学函数
    正则表达式
    view设置成圆角
    关于新浪微博注销后自动登录的问题
    根据视频地址获取某一帧的图像
    使用DES加解密
    实现通讯录的查询与删除
    iOS 动画效果
    iOS 文字下划线
  • 原文地址:https://www.cnblogs.com/wangyinghui/p/4356711.html
Copyright © 2011-2022 走看看