PQDownloadManager.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //
  2. //
  3. // PQDownloadManager.swift
  4. // PQSpeed
  5. //
  6. // Created by SanW on 2020/8/28.
  7. // Copyright © 2020 BytesFlow. All rights reserved.
  8. //
  9. import UIKit
  10. import BFCommonKit
  11. import BFNetRequestKit
  12. // MARK: - 下载管理
  13. /// 下载管理
  14. open class PQDownloadManager: NSObject {
  15. static public let shared = PQDownloadManager()
  16. public let maxDownloadCount: Int = 1000 // 最大的下载数量
  17. public var batchDownloadData: [String: Any] = Dictionary<String, Any>.init() // 批量下载数据
  18. lazy public var sessionManager: PQSessionManager = {
  19. let sessionManager = PQSessionManager("downloadConfiguration")
  20. return sessionManager
  21. }()
  22. /// <#Description#>
  23. /// - Parameters:
  24. /// - url: <#url description#>
  25. /// - name: <#name description#>
  26. /// - pathExtension: <#name description#>
  27. /// - imageURL: <#imageURL description#>
  28. /// - progressHandle: <#progressHandle description#>
  29. /// - stateHandle: <#stateHandle description#>
  30. /// - Returns: <#description#>
  31. public func download(url: String, name: String? = nil, fileExtensionType: FileExtensionType?, imageURL: String? = nil, progressHandle: @escaping ProgressHandle, stateHandle: @escaping StateHandle) {
  32. BFLog(message: "开始下载文件:\(url)")
  33. let subfile = PQDownloadFileManager.downloadTotalFile()
  34. let newFileExtensionType: FileExtensionType = fileExtensionType ?? (FileExtensionType(rawValue: url.pathExtension) ?? FileExtensionType.normal)
  35. if (subfile?.count ?? 0) > 0 && (subfile?.count ?? 0) > maxDownloadCount {
  36. PQDownloadFileManager.removeDownloadFile(url: downloadDirectory + (subfile?.first)!, fileExtensionType: newFileExtensionType)
  37. }
  38. if !isValidURL(url: url) {
  39. BFLog(message: "文件地址为空:\(url)")
  40. return
  41. }
  42. let taskId = url.md5
  43. let absolutePath = url + ".\(newFileExtensionType.rawValue)"
  44. let localPath = PQDownloadFileManager.downloadFileLocalPath(url: absolutePath, fileExtensionType: newFileExtensionType)
  45. let downloadLenght: Int64 = PQDownloadFileManager.downloadFileLength(url: absolutePath, fileExtensionType: newFileExtensionType)
  46. let downloadingTask: PQDownloadModel? = sessionManager.downloadTaskDatas.keys.contains(taskId) ? sessionManager.downloadTaskDatas[taskId] : nil
  47. var totalLength: Int64 = 0
  48. if downloadingTask != nil {
  49. totalLength = downloadingTask?.totalLength ?? 0
  50. }
  51. if downloadLenght > 0, (downloadingTask != nil && downloadingTask?.state == .compelte) || (totalLength > 0 && totalLength > downloadLenght) {
  52. progressHandle(1, downloadLenght, totalLength)
  53. BFLog(message: "文件已下载完成:\(url),downloadLenght = \(downloadLenght),totalLength = \(totalLength)")
  54. downloadingTask?.state = .compelte
  55. downloadingTask?.progress = 1
  56. postNotification(name: cDownloadMatrialSuccessKey, userInfo: ["code": "1", "url": url, "localPath": localPath, "fileExtensionType": newFileExtensionType])
  57. stateHandle(.compelte, url, localPath, nil)
  58. return
  59. }
  60. if downloadingTask != nil {
  61. var request = URLRequest(url: URL(string: url)!)
  62. request.setValue("bytes=%lld-\(downloadLenght)", forHTTPHeaderField: "Range")
  63. request.setValue("Accept-Encoding", forHTTPHeaderField: "identity")
  64. let task = sessionManager.session?.dataTask(with: request)
  65. task?.taskUrl = url
  66. task?.taskId = taskId
  67. downloadingTask?.task = task
  68. downloadingTask?.task?.resume()
  69. BFLog(message: "下载任务已存在继续下载:\(url),localPath = \(localPath)")
  70. } else {
  71. createDirectory(path: downloadDirectory)
  72. PQDownloadFileManager.removeDownloadFile(url: absolutePath, fileExtensionType: newFileExtensionType)
  73. BFLog(message: "URL(string: url)! ==\(URL(string: url)!)")
  74. var request = URLRequest(url: URL(string: url)!)
  75. // request.setValue("bytes=%lld-\(downloadLenght)", forHTTPHeaderField: "Range")
  76. request.setValue("Accept-Encoding", forHTTPHeaderField: "identity")
  77. let task = sessionManager.session?.dataTask(with: request)
  78. task?.taskUrl = url
  79. task?.taskId = taskId
  80. task?.resume()
  81. let tempModel = PQDownloadModel()
  82. tempModel.sourceURL = url
  83. tempModel.fileExtensionType = newFileExtensionType
  84. tempModel.progress = 0
  85. tempModel.state = .downloading
  86. tempModel.name = name
  87. tempModel.imageURL = imageURL
  88. tempModel.progressHandle = progressHandle
  89. tempModel.stateHandle = stateHandle
  90. tempModel.task = task
  91. BFLog(message: "新建下载任务:\(url),localPath = \(PQDownloadFileManager.downloadFileLocalPath(url: url, fileExtensionType: newFileExtensionType)),taskID:\(taskId) tempModel\(String(describing: tempModel.sourceURL))")
  92. if !sessionManager.downloadTaskDatas.keys.contains(taskId) {
  93. sessionManager.downloadTaskDatas[taskId] = tempModel
  94. }
  95. }
  96. }
  97. /// 批量下载
  98. /// - Parameters:
  99. /// - uniqueId: 每个批量下载唯一id
  100. /// - urls: 需要下载urls
  101. /// - downloadHandle: <#downloadHandle description#>
  102. /// - Returns: <#description#>
  103. public func batchDownload(uniqueId: String, urls: [PQDownloadModel], downloadHandle: @escaping (_ isSuccess: downloadState, _ msg: String?, _ data: [String: Any]?) -> Void) {
  104. BFLog(message: "urls count is \(urls.count)")
  105. if urls.count <= 0 {
  106. return
  107. }
  108. if batchDownloadData.keys.contains(uniqueId) {
  109. BFLog(message: "这组任务已经在下载中\(uniqueId)")
  110. downloadHandle(.downloading, nil, nil)
  111. return
  112. }
  113. let dispatchGroup = DispatchGroup()
  114. var downloadInfo: [String: Any] = ["dispatchGroup": dispatchGroup]
  115. for downloadUrl in urls {
  116. if isValidURL(url: downloadUrl.sourceURL) {
  117. DispatchQueue.global().async(group: dispatchGroup, execute: DispatchWorkItem(block: {
  118. dispatchGroup.enter()
  119. PQDownloadManager.shared.download(url: downloadUrl.sourceURL ?? "", fileExtensionType: downloadUrl.fileExtensionType) { _, _, _ in
  120. } stateHandle: { _, _, _, _ in
  121. }
  122. }))
  123. }
  124. }
  125. downloadInfo["urls"] = urls
  126. downloadInfo["count"] = urls.count
  127. PQDownloadManager.shared.batchDownloadData[uniqueId] = downloadInfo
  128. dispatchGroup.notify(queue: DispatchQueue.main) {
  129. BFLog(message: "所有的已请求完成,tempArr = \(PQDownloadManager.shared.batchDownloadData[uniqueId] ?? [])")
  130. postNotification(name: cBatchDownloadMatrialSuccessKey, userInfo: ["uniqueId": uniqueId, "urls": PQDownloadManager.shared.batchDownloadData[uniqueId] ?? []])
  131. PQDownloadManager.shared.batchDownloadData.removeValue(forKey: uniqueId)
  132. }
  133. }
  134. /// 下载素材成功的通知
  135. /// - Parameter notification: <#notification description#>
  136. /// - Returns: <#description#>
  137. @objc public func downloadMarial(notification: Notification) {
  138. let userInfo = notification.userInfo
  139. let url = userInfo?["url"] as? String
  140. let localPath = userInfo?["localPath"] as? String
  141. let code = userInfo?["code"] as? String
  142. BFLog(message: "目前下载的任务组数 \(PQDownloadManager.shared.batchDownloadData.keys)")
  143. PQDownloadManager.shared.batchDownloadData.forEach { _, value in
  144. var downloadInfo: [String: Any]? = value as? [String: Any]
  145. let urlsArr: [PQDownloadModel]? = downloadInfo?["urls"] as? [PQDownloadModel]
  146. let dispatchGroup: DispatchGroup? = downloadInfo?["dispatchGroup"] as? DispatchGroup
  147. var count: Int = downloadInfo?["count"] as? Int ?? 0
  148. if (urlsArr?.count ?? 0) > 0 {
  149. urlsArr?.forEach { downloadModel in
  150. if downloadModel.sourceURL == url {
  151. if code == "1" {
  152. downloadModel.filePath = localPath
  153. }
  154. BFLog(message: "count = \(count)")
  155. if count > 0 {
  156. BFLog(message: "leave = \(count)")
  157. count = count - 1
  158. downloadInfo?["count"] = count
  159. let dispatchGroupCount = dispatchGroup.debugDescription.components(separatedBy: ",").filter { $0.contains("count") }.first?.components(separatedBy: CharacterSet.decimalDigits.inverted).compactMap { Int($0) }.first
  160. BFLog(message: "dispatchGroup count is \(String(describing: dispatchGroupCount))")
  161. dispatchGroup?.leave()
  162. }
  163. }
  164. }
  165. }
  166. }
  167. }
  168. /// 下载背景音乐
  169. /// - Parameters:
  170. /// - url: <#url description#>
  171. /// - completionHandler: <#completionHandler description#>
  172. /// - Returns: <#description#>
  173. public class func downLoadFile(url: String, completionHandler: @escaping (_ filePath: String?, _ error: Error?) -> Void) {
  174. // 创建目录
  175. createDirectory(path: bgMusicDirectory)
  176. let filePath = bgMusicDirectory + url.md5 + ".mp3"
  177. let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath))
  178. if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > 0 {
  179. DispatchQueue.main.async {
  180. completionHandler(filePath, nil)
  181. }
  182. } else {
  183. let session = URLSession.shared
  184. let request = URLRequest(url: URL(string: url)!, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 30.0)
  185. let dataTask = session.downloadTask(with: request) { url, _, error in
  186. if url != nil {
  187. if FileManager.default.fileExists(atPath: filePath) {
  188. try? FileManager.default.removeItem(atPath: filePath)
  189. }
  190. try? FileManager.default.moveItem(at: url!, to: URL(fileURLWithPath: filePath))
  191. DispatchQueue.main.async {
  192. completionHandler(filePath, nil)
  193. }
  194. } else {
  195. DispatchQueue.main.async {
  196. completionHandler(nil, error)
  197. }
  198. }
  199. }
  200. dataTask.resume()
  201. }
  202. }
  203. override private init() {
  204. super.init()
  205. addNotification(self, selector: #selector(downloadMarial(notification:)), name: cDownloadMatrialSuccessKey, object: nil)
  206. }
  207. open override func copy() -> Any {
  208. return self
  209. }
  210. open override func mutableCopy() -> Any {
  211. return self
  212. }
  213. }