PQDownloadManager.swift 11 KB

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