断点续传,支持缓存和不缓存

1.导入MBProgressHUD,需要用到MBRoundProgressView这个类方法,而且在里面还要添加一个方法支持xib,

- (instancetype)initWithCoder:(NSCoder *)aDecoder

{

    if (self = [super initWithCoder:aDecoder]) {

        self.backgroundColor = [UIColor clearColor];

        self.opaque = NO;

        _progress = 0.f;

        _annular = NO;

        _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f];

        _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f];

        [self registerForKVO];

    }

    return self;

}

2.方法简单使用

 

#import "ViewController.h"

#import "MBProgressHUD.h"

#import "WFDownloadManager.h"

#define LVLibraryDirectory [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"AFALibraryMusic"]

//http://lhlsf61y6uods.oss-cn-hangzhou.aliyuncs.com/1605033142b6e5.mp3

@interface ViewController ()

@property (weak, nonatomic) IBOutlet MBRoundProgressView *progressView;

 

@end

 

@implementation ViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    

    self.progressView.progressTintColor = [UIColor grayColor];

    self.progressView.backgroundTintColor = [UIColor redColor];

    

    

}

- (IBAction)starbtn:(id)sender {

    

    NSString *url = @"http://lhlsf61y6uods.oss-cn-hangzhou.aliyuncs.com/1605033142b6e5.mp3";

    

    WFDownloadItem *item = [[WFDownloadManager shared] getDownloadItemWithUrl:url];

    if (item.state == WFDownloadState_downloading) {

        self.progressView.progress = item.progress;

        return;

    }

    

    //下载完成的回调

    WFDownloadCompletionHandler completionHandler = ^(NSString *filepath, WFDownloadState state, NSString *msg) {

        if (state == WFDownloadState_pause || state == WFDownloadState_fail) {

            NSLog(@"pause as fail");

        }else if (state == WFDownloadState_done) {

            

            self.progressView.hidden = YES;

        

            }

    

    };

    

    WFDownloadItem *downItem = [[WFDownloadManager shared] downloadItemWithUrl:url

                                                            destinationHandler:^NSString *(NSString *suggestName) {

                                                                

                                                                        return LVLibraryDirectory;

//写入文件的路径

                                                            }

                                                               progressHandler:^(double progress) {

                                                                   

//下载进度                                                                   self.progressView.progress = progress;

                                                                   

                                                               }

                                                             completionHandler:completionHandler];

    [downItem resume];

    

    

}

- (IBAction)pausebtn:(id)sender {

    

    NSString *url = @"http://lhlsf61y6uods.oss-cn-hangzhou.aliyuncs.com/1605033142b6e5.mp3";

    

     WFDownloadItem *item = [[WFDownloadManager shared] getDownloadItemWithUrl:url];

    

    if (item.state == WFDownloadState_downloading) {

        

        [item pause];

    }

    

}

 

3.下载类方法

#import <Foundation/Foundation.h>

 

typedef enum{

    WFDownloadState_downloading = 1,

    WFDownloadState_pause,

    WFDownloadState_done,

    WFDownloadState_fail

}WFDownloadState;

 

typedef NSString* (^WFDownloadDestinationHandler) (NSString *suggestName);

typedef void (^WFDownloadProgressHandler) (double progress);

typedef void (^WFDownloadCompletionHandler) (NSString *filepath, WFDownloadState state, NSString *msg);

 

#pragma mark - WFDownloadItem

@interface WFDownloadItem : NSObject

@property (strong, nonatomic) NSURLSessionDataTask *downloadTask;

@property (copy, nonatomic) NSString *urlStr;

@property (strong, nonatomic) NSFileHandle *writeHandle;

@property (assign, nonatomic) BOOL allowResumeData;     //是否允许缓存,默认YES

@property (assign, nonatomic) int64_t totalLength;

@property (assign, nonatomic) int64_t currentLength;

@property (copy, nonatomic) NSString *tmpFilepath;

@property (copy, nonatomic) NSString *defaultDestinationPath;

@property (assign, nonatomic) WFDownloadState state;

@property (assign, nonatomic) double progress;

 

@property (copy, nonatomic) WFDownloadDestinationHandler destinationHandler;

@property (copy, nonatomic) WFDownloadProgressHandler progressHandler;

@property (copy, nonatomic) WFDownloadCompletionHandler completionHandler;

 

- (void)resume;

- (void)pause;

@end

 

#pragma mark - WFDownloadManager

@interface WFDownloadManager : NSObject <NSURLSessionDataDelegate>

@property (strong, nonatomic) NSURLSession *session;

@property (strong, nonatomic) NSMutableDictionary *downloadList;    //下载列表

 

+ (WFDownloadManager *)shared;

- (NSUInteger)getFileSizeWithPath:(NSString *)path;

 

- (WFDownloadItem *)getDownloadItemWithUrl:(NSString *)urlStr;

- (void)cancelAllDownloads;

 

- (WFDownloadItem *)downloadItemWithUrl:(NSString *)urlStr

     destinationHandler:(WFDownloadDestinationHandler)destinationHandler

        progressHandler:(WFDownloadProgressHandler)progressHandler

      completionHandler:(WFDownloadCompletionHandler)completionHandler;

 

@end

 

 

#import "WFDownloadManager.h"

#import <CommonCrypto/CommonDigest.h>

 

#pragma mark - WFDownloadItem

@implementation WFDownloadItem

 

- (void)resume

{

    if (_state != WFDownloadState_downloading) {

        

        NSString *encodeStr = [_urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet];

        NSURL *url = [NSURL URLWithString:encodeStr];

        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

        //设置请求头偏移量

        _tmpFilepath = [NSString stringWithFormat:@"%@download%@.tmp", NSTemporaryDirectory(), [WFDownloadItem md5:encodeStr]];

        if (_allowResumeData) {

            NSString *range = [NSString stringWithFormat:@"bytes=%lu-", (unsigned long)[[WFDownloadManager shared] getFileSizeWithPath:_tmpFilepath]];

            [request setValue:range forHTTPHeaderField:@"Range"];

        }

        

        _downloadTask = [[WFDownloadManager shared].session dataTaskWithRequest:request];

        [[WFDownloadManager shared].downloadList setObject:self forKey:encodeStr];

        _state = WFDownloadState_downloading;

        [_downloadTask resume];

    }

}

 

- (void)pause

{

    if (_state == WFDownloadState_downloading) {

        _state = WFDownloadState_pause;

        [_downloadTask cancel];

    }

}

 

+ (NSString*)sha1:(NSString*)input

{

    const char *cStr = [input UTF8String];

    uint8_t digest[CC_SHA1_DIGEST_LENGTH];

    CC_SHA1(cStr, (int32_t)strlen(cStr), digest);

    

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

    

    for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)

        [output appendFormat:@"%02x", digest[i]];

    

    return output;

    

}

 

+ (NSString *)md5:(NSString *)input

{

    const char *cStr = [input UTF8String];

    unsigned char digest[CC_MD5_DIGEST_LENGTH];

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

    

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

    

    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)

        [output appendFormat:@"%02x", digest[i]];

    

    return output;

    

}

 

@end

 

#pragma mark - WFDownloadManager

 

@implementation WFDownloadManager

 

+ (WFDownloadManager *)shared

{

    static WFDownloadManager *manager = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        if (manager == nil) {

            manager = [[self alloc] init];

        }

    });

    return manager;

}

 

- (instancetype)init

{

    if (self = [super init]) {

        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]

                                                 delegate:self

                                            delegateQueue:NULL];

        _downloadList = [NSMutableDictionary dictionary];

    }

    return self;

}

 

- (void)cancelAllDownloads

{

    for (NSString *key in _downloadList) {

        WFDownloadItem *downloadItem = [_downloadList objectForKey:key];

        if (downloadItem.state == WFDownloadState_downloading) {

            [downloadItem pause];

        }

    }

    [_downloadList removeAllObjects];

}

 

- (WFDownloadItem *)getDownloadItemWithUrl:(NSString *)urlStr

{

    NSString *decodeStr = [urlStr stringByRemovingPercentEncoding];

    NSString *encodeStr = [decodeStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet];

    

    WFDownloadItem *item = [_downloadList objectForKey:encodeStr];

    return item;

}

 

- (WFDownloadItem *)downloadItemWithUrl:(NSString *)urlStr destinationHandler:(WFDownloadDestinationHandler)destinationHandler progressHandler:(WFDownloadProgressHandler)progressHandler completionHandler:(WFDownloadCompletionHandler)completionHandler

{

    NSString *decodeStr = [urlStr stringByRemovingPercentEncoding];//utf-8解码

    NSString *encodeStr = [decodeStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet];

    

    WFDownloadItem *downloadItem = [_downloadList objectForKey:encodeStr];

    if (downloadItem.state == WFDownloadState_downloading) {

        NSString *warningMsg = @"downloadTask is exsit!";

        dispatch_async(dispatch_get_main_queue(), ^{

            if (downloadItem.completionHandler) {

                downloadItem.completionHandler(nil, downloadItem.state, warningMsg);

            }

        });

        

        return downloadItem;

    }

 

    if (downloadItem == nil) downloadItem = [[WFDownloadItem alloc] init];

    downloadItem.urlStr = decodeStr;

    downloadItem.allowResumeData = YES;

    downloadItem.progressHandler = progressHandler;

    downloadItem.destinationHandler = destinationHandler;

    downloadItem.completionHandler = completionHandler;

    downloadItem.destinationHandler = destinationHandler;

    

    return downloadItem;

}

 

- (NSUInteger)getFileSizeWithPath:(NSString *)path

{

    NSUInteger currentSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil][NSFileSize] unsignedIntegerValue];

    return currentSize;

}

 

#pragma mark - NSURLSessionDownloadDelegate

 

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler

{

    completionHandler(NSURLSessionResponseAllow);

    

    NSString *urlStr = dataTask.originalRequest.URL.absoluteString;

    WFDownloadItem *downloadItem = [_downloadList objectForKey:urlStr];

    

    //不允许断点续传时每次都创建新的临时文件,允许断点续传时如果临时文件不存在才创建

    if (downloadItem.allowResumeData == NO || [[NSFileManager defaultManager] fileExistsAtPath:downloadItem.tmpFilepath] == NO) {

        [[NSFileManager defaultManager] createFileAtPath:downloadItem.tmpFilepath contents:nil attributes:nil];

    }

    

    downloadItem.defaultDestinationPath = [NSTemporaryDirectory() stringByAppendingPathComponent:dataTask.response.suggestedFilename];

    downloadItem.currentLength = (downloadItem.allowResumeData) ? [self getFileSizeWithPath:downloadItem.tmpFilepath] : 0;

    downloadItem.totalLength = dataTask.response.expectedContentLength + downloadItem.currentLength;

    

    downloadItem.writeHandle = [NSFileHandle fileHandleForWritingAtPath:downloadItem.tmpFilepath];

    

}

 

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data

{

    NSString *urlStr = dataTask.originalRequest.URL.absoluteString;

    WFDownloadItem *downloadItem = [_downloadList objectForKey:urlStr];

    

    BOOL status = YES;

    if ([dataTask.response isKindOfClass:[NSHTTPURLResponse class]]) {

        NSInteger statusCode = [(NSHTTPURLResponse*)dataTask.response statusCode];

        if (statusCode > 400) {

            status = NO;

        }

    }

    

    if (status) {

        

        [downloadItem.writeHandle seekToEndOfFile];

        [downloadItem.writeHandle writeData:data];

        downloadItem.currentLength += data.length;

        

        downloadItem.progress = (double)downloadItem.currentLength / downloadItem.totalLength;

        dispatch_async(dispatch_get_main_queue(), ^{

            

            if (downloadItem.progressHandler) {

                downloadItem.progressHandler(downloadItem.progress);

            }

        });

    }

}

 

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error

{

    NSString *urlStr = task.originalRequest.URL.absoluteString;

    WFDownloadItem *downloadItem = [_downloadList objectForKey:urlStr];

    [downloadItem.writeHandle closeFile];

    

    [_downloadList removeObjectForKey:urlStr];

    

    NSString *errorMsg = error.localizedDescription;

    if ([task.response isKindOfClass:[NSHTTPURLResponse class]]) {

        NSInteger statusCode = [(NSHTTPURLResponse*)task.response statusCode];

        if (statusCode > 400) {

            errorMsg = [NSString stringWithFormat:@"HTTP status code %@", @(statusCode)];

//            NSLog(@"ERROR: HTTP status code %@", @(statusCode));

        }

    }

    

    NSString *destinationFilepath = nil;

    if (errorMsg == nil) {

        if (downloadItem.destinationHandler) {

            destinationFilepath = downloadItem.destinationHandler(task.response.suggestedFilename);

        }

        

        if (destinationFilepath == nil) {

            destinationFilepath = downloadItem.defaultDestinationPath;

        }

        

        NSFileManager *fm = [NSFileManager defaultManager];

        if ([fm fileExistsAtPath:destinationFilepath]) {

            [fm removeItemAtPath:destinationFilepath error:nil];

        }

        

        NSError *error2 = nil;

        BOOL result = [fm moveItemAtPath:downloadItem.tmpFilepath toPath:destinationFilepath error:&error2];

        if (result == NO) {

            errorMsg = error2.localizedDescription;

            [fm removeItemAtPath:downloadItem.tmpFilepath error:nil];

        }

    }

    

    if (downloadItem.state != WFDownloadState_pause) {

        downloadItem.state = (errorMsg == nil) ? WFDownloadState_done : WFDownloadState_fail;

    }

    dispatch_async(dispatch_get_main_queue(), ^{

        if (downloadItem.completionHandler) {

            downloadItem.completionHandler(destinationFilepath, downloadItem.state, errorMsg);

        }

    });

}

 

@end

 

posted @ 2016-07-21 15:18  SKT_answer  阅读(588)  评论(1编辑  收藏  举报