这篇文章介绍音乐等文件的下载,支持断点续传。

我们需要创建两个类
HYDownLoader:音乐下载的主类,可以进行新建下载、暂停下载、取消下载等。
HYFileTool:文件管理类,主要为HYDownLoader服务,可以判断文件是否存在、移动文件等。

一、文件管理工具类HYFileTool

HYFileTool类比较简单,直接上代码,.h文件已经把方法备注得比较清楚了
.h文件

#import <Foundation/Foundation.h>
@interface HYFileTool : NSObject/**判断文件是否存在@param filePath 文件路径@return 是否存在*/
+(BOOL)fileExists:(NSString *)filePath;/**获取文件大小@param filePath 文件路径@return 文件大小*/
+(long long)fileSize:(NSString *)filePath;/**移动文件到新的路径@param fromPath 文件的原路径@param toPath 文件的新路径*/
+(void)moveFile:(NSString *)fromPath toPath:(NSString *)toPath;/**删除文件@param filePath 文件路径*/
+(void)removeFile:(NSString*)filePath;@end

.m文件

#import "HYFileTool.h"@implementation HYFileTool+(BOOL)fileExists:(NSString *)filePath{if (filePath.length == 0) {}return [[NSFileManager defaultManager] fileExistsAtPath:filePath];
}+(long long)fileSize:(NSString *)filePath{if (![self fileExists:filePath]) {return 0;}NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];return [fileInfo[NSFileSize] longLongValue];
}+(void)moveFile:(NSString *)fromPath toPath:(NSString *)toPath{if (![self fileExists:fromPath]) {return;}[[NSFileManager defaultManager] moveItemAtPath:fromPath toPath:toPath error:nil];
}+(void)removeFile:(NSString*)filePath{[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}@end

二、下载主类HYDownLoader.h
在HYDownLoader.h中先定义下载状态的枚举

typedef NS_ENUM(NSUInteger,HYDownloadState){HYDownloadStatePause,HYDownloadStateDowning,HYDownloadStateSuccess,HYDownloadStateFail
};

以及各种状态下的Block回调

typedef void(^DownLoadInfoType)(long long totalSize);
typedef void(^ProgressBlockType)(float progress);
typedef void(^SuccesswBlockType)(NSString *path);
typedef void(^FailBlockType)(void);
typedef void(^StateChangeBlockType)(HYDownloadState state);

然后定义对应的属性

/**下载状态*/
@property(nonatomic,assign,readonly) HYDownloadState state;/**下载进度*/
@property(nonatomic,assign,readonly) float progress;/**下载信息回调*/
@property(nonatomic,copy) DownLoadInfoType downLoadInfo;/**下载状态改变回调*/
@property(nonatomic,copy) StateChangeBlockType stateChangeInfo;/**下载进度改变回调*/
@property(nonatomic,copy) ProgressBlockType progressChange;/**下载成功回调*/
@property(nonatomic,copy) SuccesswBlockType successBlock;/**下载失败回调*/
@property(nonatomic,copy) FailBlockType failBlock;

以及方法

/**下载文件@param url 下载文件的网络地址*/
-(void)downLoader:(NSURL *)url;/**下载文件@param url 下载文件的网络地址@param downLoadInfo 下载信息block@param progressBlock 下载进度block@param successBlock 下载成功block@param failedBlock 下载失败block*/
-(void)downLoader:(NSURL *)url downLoadInfo:(DownLoadInfoType)downLoadInfo progress:(ProgressBlockType)progressBlock success:(SuccesswBlockType)successBlock failed:(FailBlockType)failedBlock;/**暂停当前任务*/
-(void)pauseCurrentTask;/**取消当前任务*/
-(void)cancelCurrentTask;/**取消并清除当前任务*/
-(void)cancelAndClean;

三、HYDownLoader.m中的变量

HYDownLoader.m中定义内部方法需要的变量

@interface HYDownLoader()<NSURLSessionDataDelegate>{long long _tempSize;//已下载文件大小long long _totalSize;//文件总大小
}@property (nonatomic,strong) NSURLSession *session;
@property (nonatomic,weak) NSURLSessionDataTask *dataTask;/**下载文件完成后的路径*/
@property (nonatomic,copy) NSString *downloadedPath;
/**下载文件时的路径*/
@property (nonatomic,copy) NSString *downloadingPath;
/**写入文件的流*/
@property (nonatomic,strong) NSOutputStream *outputStream;@end

对应的get和set方法

-(NSURLSession*)session{if (_session == nil) {NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];}return _session;
}-(void)setState:(HYDownloadState)state{if (_state == state) {return;}_state = state;if (self.stateChangeInfo) {self.stateChangeInfo(_state);}if (state == HYDownloadStateSuccess && self.successBlock) {self.successBlock(_downloadedPath);}if (state == HYDownloadStateFail && self.failBlock) {self.failBlock();}
}-(void)setProgress:(float)progress{_progress = progress;if (self.progressChange) {self.progressChange(progress);}
}

其中在setState中,我们根据不同的下载状态回调对应的Block通知外界。

四、HYDownLoader.m的主要下载方法downLoader:和downloadWithURL:offset:

-(void)downLoader:(NSURL *)url{//如果任务存在,当前只是暂停,则继续下载if ([url isEqual:self.dataTask.originalRequest.URL] && self.state == HYDownloadStatePause) {[self resumeCurrenttask];return;}//1.文件的存放//下载时存放到temp(此目录用于存放临时文件,app退出时会被清理)//下载完成后移动到cache(iTunes不会备份此目录,此目录下文件不会在app退出时删除)NSString *fileName = url.lastPathComponent;self.downloadedPath = [kCachePath stringByAppendingPathComponent:fileName];self.downloadingPath = [kTmpPath stringByAppendingPathComponent:fileName];//1.判断url地址对应的资源是否已下载完成//1.1如果已完成,则返回相关信息if([HYFileTool fileExists:self.downloadedPath]){NSLog(@"已下载完成(文件已存在)");self.state = HYDownloadStateSuccess;return;}[self downloadWithURL:url offset:0];//2.否则检查临时文件是否存在//2.1若存在,以当前已存在文件大小,作为开始字节请求资源。if ([HYFileTool fileExists:self.downloadingPath]) {//获取本地文件大小(已下载部分)_tempSize = [HYFileTool fileSize:self.downloadingPath];[self downloadWithURL:url offset:_tempSize];return;}// 本地大小 == 总大小 则移动到cache文件夹// 本地大小 > 总大小  则删除本地缓存,重新从0开始下载// 本地大小 < 总大小  从本地大小开始下载//2.2 不存在,则从0字节开始请求资源[self downloadWithURL:url offset:0];}

再写带有回调block的方法,此时只要给各个block赋值, 并调用上面下载的主方法:

-(void)downLoader:(NSURL *)url downLoadInfo:(DownLoadInfoType)downLoadInfo progress:(ProgressBlockType)progressBlock success:(SuccesswBlockType)successBlock failed:(FailBlockType)failedBlock{self.downLoadInfo = downLoadInfo;self.progressChange = progressBlock;self.successBlock = successBlock;self.failBlock  = failedBlock;[self downLoader:url];
}

downLoader:方法只是对当前状态做了判断,下载的核心其实是downloadWithURL:offset:方法。使用系统的NSURLSession进行网络请求。 HTTP1.1 协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在 Header 里两个参数实现的,客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。所以断点续传的请求的关键在于,给request的请求头设置Range,表示只请求offset后面的数据。

/**根据开始字节,请求资源@param url 下载url@param offset 开始字节*/
- (void)downloadWithURL:(NSURL *)url offset:(long long)offset{NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:0];[request setValue:[NSString stringWithFormat:@"bytes=%lld-",offset] forHTTPHeaderField:@"Range"];self.dataTask = [self.session dataTaskWithRequest:request];[self resumeCurrenttask];
}

NSURLSession的请求回调为代理方式。HYDownLoader遵循NSURLSessionDataDelegate代理,并完成其中的三个代理回调:

#pragma mark - 协议//接收到响应头
- (void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSHTTPURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler{NSLog(@"%@",response);_totalSize = [response.allHeaderFields[@"Content-Length"] longLongValue];NSString *contentRangeStr = response.allHeaderFields[@"Content-Range"];if (contentRangeStr.length > 0) {_totalSize = [[[contentRangeStr componentsSeparatedByString:@"/"] lastObject] longLongValue];}self.downLoadInfo(_totalSize);if (_tempSize == _totalSize) {//文件移动到完成文件夹NSLog(@"下载完成,移动文件到完成文件夹");[HYFileTool moveFile:_downloadingPath toPath:_downloadedPath];completionHandler(NSURLSessionResponseCancel);self.state = HYDownloadStateSuccess;return;}if (_tempSize > _totalSize) {//删除临时缓存[HYFileTool removeFile:self.downloadingPath];//重新下载[self downLoader:response.URL];//取消请求completionHandler(NSURLSessionResponseCancel);}//继续接受数据self.state = HYDownloadStateDowning;self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.downloadingPath append:YES];completionHandler(NSURLSessionResponseAllow);
}//继续接收数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{_tempSize += data.length;self.progress = 1.0 * _tempSize / _totalSize;[self.outputStream write:data.bytes maxLength:data.length];NSLog(@"接受数据");
}//请求结束
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{NSLog(@"接收完成");if (error == nil) {[HYFileTool moveFile:self.downloadingPath toPath:self.downloadedPath];self.state = HYDownloadStateSuccess;}else{if(error.code == -999){NSLog(@"取消下载");self.state = HYDownloadStatePause;}else{NSLog(@"下载错误%@",error);self.state = HYDownloadStateFail;}}[self.outputStream close];
}

其中的核心点我们详细解释下:

  1. 在didReceiveResponse接受到头信息后,通过completionHandler回调,决定对后续内容的操作,如取消请求或继续接收数据
  2. 通过outputStream流的方式写入文件。
  3. 文件接收中止后会回调didCompleteWithError:方法,这时候有三种情况:正常完成、用户取消、异常中止等,根据error可以分别判断。

完整代码:
https://github.com/dolacmeng/HYDownLoader

音乐(文件)断点下载相关推荐

  1. 使用NSURLConnection实现大文件断点下载

    使用NSURLConnection实现大文件断点下载 由于是实现大文件的断点下载,不是下载一般图片什么的.在设计这个类的时候本身就不会考虑把下载的文件缓存到内存中,而是直接写到文件系统. 要实现断点下 ...

  2. NSURLSessionDataTask与NSOperationQueue实现多文件断点下载(任意时刻终止进程,重启应用,自动重启下载)...

    效果展示 gif有点大,直接连接:7qnbrb.com1.z0.glb.clouddn.com/download.gi- 知识要点 NSOperationQueue线程队列的管理 NSURLSessi ...

  3. SpringBoot+Vue 实现大文件断点下载

    目录 效果 代码 SpringBoot Vue 效果 步骤 点击下载 在输入框输入下载的文件名称 点击暂停 再次点击开始 下载完成 代码 SpringBoot pom <!-- 做断点下载使用- ...

  4. python实现文件断点下载

    1.需求: 实现文件的断点下载 2.实现: #!/usr/bin/python # encoding=utf-8 import requests, sys, os, re, timeclass dow ...

  5. Retrofit 2.0 超能实践(四),完成大文件断点下载

    作者:码小白 文/CSDN 博客 本文出自:http://blog.csdn.net/sk719887916/article/details/51988507 码小白 通过前几篇系统的介绍和综合运用, ...

  6. 基于android的网络音乐播放器-下载完成后下拉音乐列表刷新(八)

    作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩 ...

  7. 基于android的网络音乐播放器-网络音乐的多线程下载(六)

    作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩 ...

  8. 如何分割cue+ape、cue+flac、cue+wav、cue+dts整轨音乐文件

    如何分割cue+ape.cue+flac.cue+wav.cue+dts整轨音乐文件 网上下载的音乐专辑,不少是"cue+整轨音乐"文件形式,常见的有APE+CUE.WAV+CUE ...

  9. Android文件下载——单线程断点下载

    文章目录 1. 相关逻辑 1.1 获取待下载文件的总长度 1.2 使用HTTP的Range头部字段 1.3 RandomAccessFile 1.4 添加监听接口 2. 调用示例 3. DownLoa ...

最新文章

  1. 人工智能尴尬的2019:需要钱却没钱可烧了
  2. 2017阿里云TECH INSIGHT干货分享第六届隆重回归
  3. Xamarin XAML语言教程Xamarin.Forms中改变活动指示器颜色
  4. 皮一皮:沉迷游戏的下场...
  5. vectorvn1610报价_【8.5873.5444.G323】价格_厂家 - 中国供应商
  6. 转https_免费!这个PPT转PDF的方法不学可惜!
  7. java中解密技术是什么_详解Java 加密解密技术的分类和归纳
  8. 博主已开启评论精选什么意思_小白必看!想要成为小红书博主,首先要掌握4个工具!...
  9. 多个canvas画布合并_canvas的基础入门
  10. 用python计算1~100的阶乘之和_在Python中递归函数调用举例and匿名函数lambda求1~100的和及计算阶乘举例...
  11. java线程集合点_Jmeter集合点(线程组和同步定时器)
  12. Flex 国际化使用
  13. 江苏教师计算机考试报名时间2015下半年,2021年江苏教师资格证报名时间入口-报考条件-面试报名-233网校...
  14. 10录屏电流声_到底有没有完全免费的电脑+手机(安卓)录屏神器推荐,要能录制系统内部声音+麦克风的?...
  15. (转)星巴克其实是靠大数据盈利的!
  16. JavaScript中使用attachEvent实现事件监听
  17. excel 将日期转换为8位数字
  18. 左岸读书-你是想读书,还是想读完书?
  19. STK Components规格书
  20. 配置 NFS 网络文件系统

热门文章

  1. jquery-ajax请求:超时设置,增加 loading 提升体验
  2. jquery选择器的使用方式
  3. DDD领域驱动设计之聚合、实体、值对象
  4. ruby Mixin用法
  5. 关闭Windows 2000/XP/2003默认共享
  6. 系统架构的过程 浮现式设计
  7. C# 获取 IE 临时文件
  8. c#如何取自身应用程序文件名和路径?
  9. C++拾趣——STL容器的插入、删除、遍历和查找操作性能对比(ubuntu g++)——删除
  10. OpenCV中的内存泄露问题(cvLoadImage,cvCloneImage)