利用NSURLSession完成的断点续传功能
首先是业务中的.h文件
#import <UIKit/UIKit.h> #import "DenglSingleton.h" @protocol DownLoadVCdelegate <NSObject> -(void)DownloadCompletedWithIndex:(NSInteger)index; @end @interface DownLoadVC : UIViewController DenglSingletonH(DownLoadVC) @property (nonatomic,copy) NSDictionary *dic; @property (nonatomic,weak) id<DownLoadVCdelegate> delegate; @end
#import "DownLoadVC.h" #import "UIView+denglFrame.h" #import "DBBackgroundBtn.h" #import "SVProgressHUD.h" #define dAllMusicPlistFilePath [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:@"text"] stringByAppendingPathComponent:@"allmusic.plist"] #define filePath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:self.fileName] @interface DownLoadVC ()<NSURLSessionDataDelegate> { NSString *_downloadTag; } @property (strong, nonatomic) UIImageView *iconImageView; @property (strong, nonatomic) UIImageView *bottomImageView; @property (strong, nonatomic) UITextView *AppDescritionTextView; @property (strong, nonatomic) UILabel *AppNameLabel; @property (strong, nonatomic) DBBackgroundBtn *downLoadBtn; @property (nonatomic,strong) UIButton *backBtn; @property (nonatomic,strong) UIImageView *backBtnImgView; @property (nonatomic,copy) NSString *currentName; /** 下载任务 */ @property (nonatomic, strong) NSURLSessionDataTask *task; /** session */ @property (nonatomic, strong) NSURLSession *session; /** 写文件的流对象 */ @property (nonatomic, strong) NSOutputStream *stream; /** 文件的总长度 */ @property (nonatomic, assign) NSInteger totalLength; /** 文件百分比 */ @property (nonatomic, assign) float p; /** 文件名字 */ @property (nonatomic, copy) NSString *fileName; /** 文件url */ @property (nonatomic, strong) NSURL *url; /** 下载数据保存字典 */ @property (nonatomic, strong) NSMutableDictionary *downloadingListDic; @end @implementation DownLoadVC DenglSingletonM(DownLoadVC) - (NSURLSession *)session { if (!_session) { _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]]; } return _session; } - (NSOutputStream *)stream { if (!_stream) { _stream = [NSOutputStream outputStreamToFileAtPath:filePath append:YES]; } return _stream; } -(void)startDownloadMusic{ // 创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url]; // 设置请求头 // Range : bytes=xxx-xxx NSInteger downloadLength = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil][NSFileSize] integerValue]; NSString *range = [NSString stringWithFormat:@"bytes=%zd-", downloadLength]; [request setValue:range forHTTPHeaderField:@"Range"]; // 创建一个Data任务 self.task = [self.session dataTaskWithRequest:request]; // NSLog(@"test"); } -(void)setDic:(NSDictionary *)dic{ _dic = dic; self.fileName = [self.dic objectForKey:@"filename"]; self.url = [NSURL URLWithString:[self.dic objectForKey:@"url"]]; if (self.currentName == nil) { //初始化UI [self setUI]; self.currentName = self.fileName; //取出上一次保存的下载进度 self.downloadingListDic = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults]objectForKey:@"downloadingListDic"]]; if (self.downloadingListDic == nil) { self.downloadingListDic = [NSMutableDictionary dictionary]; [[NSUserDefaults standardUserDefaults] setObject:self.downloadingListDic forKey:@"downloadingListDic"]; [[NSUserDefaults standardUserDefaults]synchronize]; } self.p = [[self.downloadingListDic objectForKey:self.fileName] floatValue]; self.downLoadBtn.progress = self.p; // if (self.p != 0) { [self.downLoadBtn setBackgroundImage:[UIImage imageNamed:@"download_pause"] forState:UIControlStateNormal]; } }else{ if (![self.currentName isEqualToString:self.fileName]) { //取消任务 self.currentName = self.fileName; [self.session invalidateAndCancel]; self.session = nil; //取出上一次保存的下载进度 self.downloadingListDic = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults]objectForKey:@"downloadingListDic"]]; if (self.downloadingListDic == nil) { self.downloadingListDic = [NSMutableDictionary dictionary]; [[NSUserDefaults standardUserDefaults] setObject:self.downloadingListDic forKey:@"downloadingListDic"]; [[NSUserDefaults standardUserDefaults]synchronize]; } self.p = [[self.downloadingListDic objectForKey:self.fileName] floatValue]; self.downLoadBtn.progress = self.p; // if (self.p != 0) { [self.downLoadBtn setBackgroundImage:[UIImage imageNamed:@"download_pause"] forState:UIControlStateNormal]; } self.downLoadBtn.tag = 1; } } [self setDataWithDic:dic]; } - (void)viewDidLoad { [super viewDidLoad]; } -(void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; [self.downloadingListDic setObject:[NSString stringWithFormat:@"%f",self.p] forKey:self.fileName]; [[NSUserDefaults standardUserDefaults] setObject:self.downloadingListDic forKey:@"downloadingListDic"]; [[NSUserDefaults standardUserDefaults]synchronize]; } -(void)setUI{ [[NSUserDefaults standardUserDefaults]setObject:@"no" forKey:@"begin"]; self.view.backgroundColor = [UIColor whiteColor]; //返回按钮 self.backBtn = [[UIButton alloc]init]; [self.backBtn addTarget:self action:@selector(clickBackBtn) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:self.backBtn]; // self.AppNameLabel = [[UILabel alloc]init]; self.AppNameLabel.textAlignment = NSTextAlignmentCenter; [self.view addSubview:self.AppNameLabel]; // self.AppDescritionTextView = [[UITextView alloc]init]; self.AppDescritionTextView.textColor = [UIColor grayColor]; self.AppDescritionTextView.editable = NO; self.AppDescritionTextView.selectable = NO; [self.view addSubview:self.AppDescritionTextView]; // self.bottomImageView = [[UIImageView alloc]init]; [self.view addSubview:self.bottomImageView]; // self.iconImageView = [[UIImageView alloc]init]; [self.view addSubview:self.iconImageView]; // self.downLoadBtn = [[DBBackgroundBtn alloc]init]; [self.downLoadBtn setBackgroundImage:[UIImage imageNamed:@"download"] forState:UIControlStateNormal]; self.downLoadBtn.tag = 1; self.downLoadBtn.progress = 0; [self.downLoadBtn addTarget:self action:@selector(clickdownLoadBtn:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:self.downLoadBtn]; // self.backBtnImgView = [[UIImageView alloc]init]; self.backBtnImgView.image = [UIImage imageNamed:@"back_ipad.png"]; [self.view addSubview:self.backBtnImgView]; } -(void)viewDidLayoutSubviews{ [super viewDidLayoutSubviews]; [self setUIFrame]; } -(void)setUIFrame{ //下载按钮 self.downLoadBtn.width = 100 / 1136.0 * self.view.height; self.downLoadBtn.height = self.downLoadBtn.width; self.downLoadBtn.x = (self.view.width - self.downLoadBtn.width)/2; self.downLoadBtn.y = 970 / 1136.0 * self.view.height; //图标 self.iconImageView.width = 400 / 1136.0 * self.view.height; self.iconImageView.height = self.iconImageView.width; self.iconImageView.x = (self.view.width - self.iconImageView.width)/2; self.iconImageView.y = 128 / 1136.0 * self.view.height; //底部图 self.bottomImageView.width = self.view.width; self.bottomImageView.height = 240 / 1136.0 * self.view.height; self.bottomImageView.x = 0; self.bottomImageView.y = 900 / 1136.0 * self.view.height; //AppNameLabel app名称 self.AppNameLabel.width = self.view.width; self.AppNameLabel.height = 36 / 1136.0 * self.view.height; self.AppNameLabel.x = 0; self.AppNameLabel.y = 585 / 1136.0 * self.view.height; self.AppNameLabel.font = [UIFont systemFontOfSize:35 / 1136.0 * self.view.height]; //AppDescritionTextView self.AppDescritionTextView.x = 35 / 1136.0 * self.view.height; self.AppDescritionTextView.width = self.view.width - self.AppDescritionTextView.x * 2; self.AppDescritionTextView.height = 250 / 1136.0 * self.view.height; self.AppDescritionTextView.y = 640 / 1136.0 * self.view.height; [self.AppDescritionTextView scrollRangeToVisible:NSMakeRange(0, 1)]; // self.AppDescritionTextView.font = [UIFont systemFontOfSize:32 / 1136.0 * self.view.height]; //返回按钮 self.backBtn.width = 50; self.backBtn.height = 50; self.backBtn.x = 0; self.backBtn.y = 0; // 15 8 12 25 self.backBtnImgView.width = 12 / 568.0 * self.view.height; self.backBtnImgView.height = 25 / 568.0 * self.view.height; self.backBtnImgView.x = 15/ 568.0 * self.view.height; self.backBtnImgView.y = 8/ 568.0 * self.view.height; } -(void)setDataWithDic:(NSDictionary*)dic{ // NSString *iconImage = [dic objectForKey:@"image"]; self.iconImageView.image = [UIImage imageNamed:iconImage]; // NSString *bottomImage = [dic objectForKey:@"color"]; self.bottomImageView.image = [UIImage imageNamed:bottomImage]; // NSArray *AppNameArr = [dic objectForKey:@"name"]; NSInteger index = [[NSUserDefaults standardUserDefaults] integerForKey:@"yuyan"]; self.AppNameLabel.text = [AppNameArr objectAtIndex:index]; // NSArray *AppDescritionArr = [dic objectForKey:@"info"]; self.AppDescritionTextView.text = [AppDescritionArr objectAtIndex:index]; } - (IBAction)clickdownLoadBtn:(id)sender { UIButton *btn = sender; if (btn.tag == 1) {//下载 [self.downLoadBtn setBackgroundImage:[UIImage imageNamed:@"download"] forState:UIControlStateNormal]; [self startDownloadMusic]; [self.task resume]; _downloadTag = @"resume"; }else if (btn.tag == -1){//停止 [self.downLoadBtn setBackgroundImage:[UIImage imageNamed:@"download_pause"] forState:UIControlStateNormal]; if (self.session) { [self.session invalidateAndCancel]; self.session =nil; } _downloadTag = @"stop"; } btn.tag *= -1; } -(void)dealloc{ } - (void)clickBackBtn { [self.navigationController popViewControllerAnimated:YES]; } #pragma mark - NSURLSessionDataDelegate /** * 1.接收到响应 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { // 获得服务器这次请求 返回数据的总长度 NSInteger downloadLength = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil][NSFileSize] integerValue]; self.totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + downloadLength; // 打开流 [self.stream open]; // 接收这个请求,允许接收服务器的数据 completionHandler(NSURLSessionResponseAllow); } /** * 2.接收到服务器返回的数据 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.stream write:data.bytes maxLength:data.length]; // 下载进度 NSInteger downloadLength = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil][NSFileSize] integerValue]; self.p = 1.0 * downloadLength / self.totalLength; NSLog(@"%f",self.p); dispatch_async(dispatch_get_main_queue(), ^{ self.downLoadBtn.progress = self.p; }); } /** * 3.请求完毕(成功\失败) */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { // 关闭流 [self.stream close]; self.stream = nil; if (!error) {//没有错误信息 if (task.state == NSURLSessionTaskStateCompleted) { NSLog(@"NSURLSessionTaskStateCompleted"); NSInteger index = [[self.dic objectForKey:@"index"] integerValue]; //下载完成后,将曲目的下载状态做一次更新,将新的数组更新到plist文件中。 NSMutableArray *arr = [NSMutableArray arrayWithContentsOfFile:dAllMusicPlistFilePath]; NSMutableDictionary *dic = [arr objectAtIndex:index]; [dic setObject:@"1" forKey:@"isDownLoad"]; if ([arr writeToFile:dAllMusicPlistFilePath atomically:YES]) { NSLog(@"writeToFileSuccess"); } dispatch_async(dispatch_get_main_queue(), ^{ if (self.delegate && [self.delegate respondsToSelector:@selector(DownloadCompletedWithIndex:)]) { [self.delegate DownloadCompletedWithIndex:index]; } [self.navigationController popViewControllerAnimated:NO]; }); } }else{//有错误信息 if (error.code == 18446744073709550617) { NSLog(@"用户取消"); }else{ [SVProgressHUD setMinimumDismissTimeInterval:3.5]; [SVProgressHUD setDefaultStyle:SVProgressHUDStyleDark]; NSString *str = NSLocalizedString(@"loadFaild", nil); [SVProgressHUD showErrorWithStatus:str]; } } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步