首页 > 代码库 > iOS 文件下载

iOS 文件下载

最为原始的文件下载
第一步:
使用MD5对url加密
NSString+Password.h

#import <Foundation/Foundation.h>

@interface NSString (Password)

/**
 *  32位MD5加密
 *
 *  @return 32位MD5加密结果
 */
- (NSString *)MD5;

/**
 *  SHA1加密
 *
 *  @return SHA1加密结果
 */
- (NSString *)SHA1;

@end
#import "NSString+Password.h"
#import <CommonCrypto/CommonDigest.h>

@implementation NSString (Password)

#pragma mark 使用MD5加密字符串
- (NSString *)MD5
{
    const char *cStr = [self UTF8String];
    unsigned char digest[CC_MD5_DIGEST_LENGTH];

    CC_MD5(cStr, strlen(cStr), digest);

    NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];

    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [result appendFormat:@"%02x", digest[i]];
    }

    return result;
}

#pragma mark 使用SHA1加密字符串
- (NSString *)SHA1
{
    const char *cStr = [self UTF8String];
    NSData *data = http://www.mamicode.com/[NSData dataWithBytes:cStr length:self.length];
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];

    CC_SHA1(data.bytes, data.length, digest);

    NSMutableString *result = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];

    for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
        [result appendFormat:@"%02x", digest[i]];
    }

    return result;
}

@end

封装下载工具类

FileDownload
#import <Foundation/Foundation.h>

@interface FileDownload : NSObject

- (void)downloadFileWithURL:(NSURL *)url completion:(void (^)(UIImage *image))completion;
@end
#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 = http://www.mamicode.com/1,  // 忽略本地的内存缓存>
- (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----%@", range);

    NSURLResponse *response = nil;
    NSData *data = http://www.mamicode.com/[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];

    // 写入文件,覆盖文件不会追加
//    [data writeToFile:@"/Users/aplle/Desktop/1.png" atomically:YES];
    [self appendData:data];

    NSLog(@"response*******%@", 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

使用工具类进行下载文件

#import "MJViewController.h"
#import "FileDownload.h"

@interface MJViewController ()
@property (nonatomic, strong) FileDownload *download;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end

@implementation MJViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.download = [[FileDownload alloc] init];
    // http://image.zcool.com.cn/56/13/1308200901454.jpg
    [self.download downloadFileWithURL:[NSURL URLWithString:@"http://image.zcool.com.cn/56/13/1308200901454.jpg"] completion:^(UIImage *image) {

        self.imageView.image = image;
    }];
}

@end
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

iOS 文件下载