WSDownloadSession.m 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. //
  2. // HSNSURLSession.m
  3. // HSDownloadManagerExample
  4. //
  5. // Created by hans on 15/8/4.
  6. // Copyright © 2015年 hans. All rights reserved.
  7. //
  8. #import "WSDownloadSession.h"
  9. #define CunCount 8*1024
  10. @interface WSDownloadSession()<NSURLSessionDelegate>
  11. @property (nonatomic,copy)NSString *msgUUID;
  12. /** 流 */
  13. @property (nonatomic, strong) NSOutputStream *stream;
  14. //下载任务
  15. @property (nonatomic, strong) NSURLSessionDataTask *task;
  16. @property (nonatomic , strong) NSMutableURLRequest *request;
  17. /** 下载地址 */
  18. @property (nonatomic, copy) NSString *url;
  19. /**
  20. * 缓存根路径/A/
  21. */
  22. @property (nonatomic , copy) NSString *cacheRootPath;
  23. /**
  24. * 绝对路径(最后的两个绝对,用来拼接)xxj/之后的路径,(需要传出来,所以带上filename)
  25. */
  26. @property (nonatomic , copy) NSString *localPath;
  27. @property (nonatomic , copy) NSString *attachName;
  28. @property (nonatomic , copy) NSString *fileMd5;
  29. /**
  30. * 附件完整路径/A/xx.yy
  31. */
  32. @property (nonatomic , copy) NSString *attachFullPath;
  33. /**
  34. * 文件大小描述文件完全路径
  35. */
  36. @property (nonatomic , copy) NSString *attachTotalLengthFileFullPath;
  37. /**
  38. * 已下载长度
  39. */
  40. @property (nonatomic , assign)NSInteger downloadedLength;
  41. /** 获得服务器这次请求 返回数据的总长度 */
  42. @property (nonatomic, assign) NSInteger totalLengthSaveFromServer;
  43. //请求时文件模型告知的实际大小.
  44. @property (nonatomic , assign) NSInteger totalLengthNeedDownload;
  45. @property (nonatomic, assign)NSURLSession *session;
  46. @end
  47. //取消
  48. static const NSUInteger SessionCancerCode = -999;
  49. //超时
  50. @implementation WSDownloadSession
  51. + (id)oneDownloadSessionWithFileUrlString:(NSString *)downloadUrl savePath:(NSString *)path localPath:(NSString *)localPath fileMd5:(NSString *)md5 UUID:(NSString *)uuid fileName:(NSString *)fileName downloadedSize:(NSInteger)downloadedSize fileSize:(NSInteger)fileSize progressblock:(DownloadProgressBlock)progressBlock successblock:(DownloadSuccessBlock)successblock failedBlock:(DownloadFailedBlock)failedBlock;
  52. {
  53. WSDownloadSession *wsDowunloadSession = [[WSDownloadSession alloc]initOneDownloadSessionWithFileUrlString:downloadUrl savePath:path localPath:localPath fileMd5:md5 UUID:uuid fileName:fileName downloadedSize:downloadedSize fileSize:fileSize progressblock:progressBlock successblock:successblock failedBlock:failedBlock];
  54. return wsDowunloadSession;
  55. }
  56. //SAVE PATH: .../Library/xxj/cloudstorage
  57. //localpath /cloudstorage/filename
  58. // savepath/ localpath /filename
  59. //...library/xxj/cloudStorage/dada78dafada/xxx.filename
  60. - (instancetype)initOneDownloadSessionWithFileUrlString:(NSString *)downloadUrl savePath:(NSString *)savePath localPath:(NSString *)localPath fileMd5:(NSString *)md5 UUID:(NSString *)uuid fileName:(NSString *)fileName downloadedSize:(NSInteger)downloadedSize fileSize:(NSInteger)fileSize progressblock:(DownloadProgressBlock)progressBlock successblock:(DownloadSuccessBlock)successblock failedBlock:(DownloadFailedBlock)failedBlock
  61. {
  62. if (self = [super init]) {
  63. self.msgUUID = uuid;
  64. self.fileMd5 = md5;
  65. self.url = downloadUrl;
  66. self.totalLengthNeedDownload = fileSize;
  67. /**
  68. * 实际效果不大?,还是读取实际的大小.
  69. */
  70. self.downloadedLength = downloadedSize;
  71. self.downloadProgressBlock = progressBlock;
  72. self.downloadSuccessBlock = successblock;
  73. self.downloadFailedBlock = failedBlock;
  74. self.attachName = fileName;
  75. //localpath即本地绝对localpath,localpath可以不同,name可以相同.
  76. self.localPath = localPath;//![localPath isEqualToString:fileName]?[NSString stringWithFormat:@"%@/%@",localPath,fileName]:fileName;//存入数据库中.
  77. //下载完注意删除
  78. self.attachTotalLengthFileFullPath = [savePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%@_totalLength.plist",fileName,md5]];
  79. self.attachFullPath = [NSString stringWithFormat:@"%@/%@",savePath,self.localPath];
  80. self.cacheRootPath = self.attachFullPath.stringByDeletingLastPathComponent;
  81. [self createCacheDirectory];
  82. NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc]init]];
  83. //写入文件专用.
  84. NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:self.attachFullPath append:YES];
  85. //中文字符的链接处理下
  86. // 创建请求
  87. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[downloadUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
  88. self.stream = stream;
  89. self.session = session;
  90. self.request = request;
  91. [self sessionWithNewRequest];
  92. }
  93. return self;
  94. }
  95. - (void)sessionWithNewRequest
  96. {
  97. //中文字符的链接处理下
  98. // 创建请求
  99. NSInteger maxDownloadLength = self.downloadedLength + CunCount;
  100. if (maxDownloadLength > self.totalLengthNeedDownload) {
  101. maxDownloadLength = self.totalLengthNeedDownload;
  102. }
  103. // 设置请求头
  104. NSString *range = [NSString stringWithFormat:@"bytes=%ld-%ld",self.downloadedLength,maxDownloadLength];
  105. [self.request setValue:range forHTTPHeaderField:@"Range"];
  106. NSURLSessionDataTask *task = [self.session dataTaskWithRequest:self.request];
  107. [task resume];
  108. self.task = task;
  109. }
  110. /**
  111. * 创建缓存目录文件
  112. */
  113. - (void)createCacheDirectory
  114. {
  115. NSFileManager *fileManager = [NSFileManager defaultManager];
  116. if (![fileManager fileExistsAtPath:self.cacheRootPath]) {
  117. [fileManager createDirectoryAtPath:self.cacheRootPath withIntermediateDirectories:YES attributes:nil error:NULL];
  118. }
  119. }
  120. //实时获取文件大小
  121. - (NSInteger)downloadedLength
  122. {
  123. return [[[NSFileManager defaultManager] attributesOfItemAtPath:self.attachFullPath error:nil][NSFileSize] integerValue];
  124. }
  125. //挂起.
  126. - (void)handle
  127. {
  128. if (self.task.state == NSURLSessionTaskStateRunning) {
  129. [self pause];
  130. } else {
  131. [self start];
  132. }
  133. }
  134. /**
  135. * 开始下载
  136. */
  137. - (void)start
  138. {
  139. [self.task resume];
  140. }
  141. /**
  142. * 暂停下载-如果url相同的话就有问题.
  143. */
  144. - (void)pause
  145. {
  146. //可能超时,show error,直接cancer吧。
  147. if (!self.task) {
  148. return;
  149. }
  150. [self.task suspend];
  151. }
  152. - (void)cancer
  153. {
  154. if (!self.task) {
  155. return;
  156. }
  157. [self.session finishTasksAndInvalidate];
  158. [self.session invalidateAndCancel];
  159. // 关闭流
  160. [self.stream close];
  161. self.stream.delegate = nil;
  162. // 清除任务
  163. [self.task cancel];
  164. self.task = nil;
  165. self.stream = nil;
  166. self.session = nil;
  167. }
  168. /**
  169. * 判断该文件是否下载完成
  170. */
  171. - (BOOL)isCompletion
  172. {
  173. if (self.downloadedLength == self.totalLengthNeedDownload ) {
  174. return YES;
  175. }
  176. return NO;
  177. }
  178. /**
  179. * 根据服务端返回的总大小生成的文件 获取该资源总大小
  180. */
  181. - (NSInteger)fileTotalLength
  182. {
  183. return [[NSDictionary dictionaryWithContentsOfFile:self.attachTotalLengthFileFullPath][self.attachName] integerValue];
  184. }
  185. #pragma mrak --
  186. #pragma mark -- URLSessionDelegate
  187. /**
  188. * 接收到响应
  189. */
  190. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
  191. {
  192. // 打开流
  193. if (self.stream.streamStatus != NSStreamStatusOpen) {
  194. [self.stream open];
  195. }
  196. // 接收这个请求,允许接收服务器的数据
  197. completionHandler(NSURLSessionResponseAllow);
  198. }
  199. /**
  200. * 接收到服务器返回的数据
  201. */
  202. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
  203. {
  204. // 写入数据
  205. [self.stream write:data.bytes maxLength:data.length];
  206. self.downloadedLength += data.length;
  207. // 下载进度
  208. NSUInteger receivedSize = self.downloadedLength;
  209. NSUInteger expectedSize = self.totalLengthNeedDownload;
  210. CGFloat progress = 1.0 * receivedSize / expectedSize;
  211. if (self.downloadProgressBlock) {
  212. self.downloadProgressBlock(receivedSize, expectedSize, progress,self.attachFullPath,self.msgUUID);
  213. }
  214. }
  215. /**
  216. * 请求完毕(成功|失败)
  217. */
  218. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
  219. {
  220. __weak typeof(self)weakself = self;
  221. task = nil;
  222. if ([self isCompletion]) {
  223. // 下载完成
  224. if (weakself.downloadSuccessBlock) {
  225. weakself.downloadSuccessBlock(weakself.downloadedLength,weakself.localPath,weakself.msgUUID);
  226. }
  227. [self closeSession];
  228. }else if (self.downloadedLength < self.totalLengthNeedDownload && !error)
  229. {
  230. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  231. if(weakself.session)[weakself sessionWithNewRequest];
  232. });
  233. }else if (error && error.code != SessionCancerCode){
  234. // 下载失败
  235. if (self.downloadFailedBlock) {
  236. self.downloadFailedBlock(@"error",self.msgUUID);
  237. }
  238. [self closeSession];
  239. }
  240. }
  241. - (void)closeSession
  242. {
  243. [self.session finishTasksAndInvalidate];
  244. [self.session invalidateAndCancel];
  245. // 关闭流
  246. [self.stream close];
  247. self.stream.delegate = nil;
  248. // 清除任务
  249. [self.task cancel];
  250. self.request = nil;
  251. self.task = nil;
  252. self.stream = nil;
  253. self.session = nil;
  254. }
  255. - (void)dealloc
  256. {
  257. }
  258. @end