// // EPImageUploader.swift // YuMi // // Created by AI on 2025-10-11. // import UIKit import Foundation /// 图片批量上传工具(纯 Swift 内部类,直接使用 QCloudCOSXML SDK) /// 不对外暴露,由 EPSDKManager 内部调用 class EPImageUploader { init() {} /// 批量上传图片(内部方法) /// - Parameters: /// - images: 要上传的图片数组 /// - bucket: QCloud bucket 名称 /// - customDomain: 自定义域名 /// - progress: 进度回调 (已上传数, 总数) /// - success: 成功回调 /// - failure: 失败回调 func performBatchUpload( _ images: [UIImage], bucket: String, customDomain: String, progress: @escaping (Int, Int) -> Void, success: @escaping ([[String: Any]]) -> Void, failure: @escaping (String) -> Void ) { let total = images.count let queue = DispatchQueue(label: "com.yumi.imageupload", attributes: .concurrent) let semaphore = DispatchSemaphore(value: 3) // 最多同时上传 3 张 var uploadedCount = 0 var resultList: [[String: Any]] = [] var hasError = false let lock = NSLock() for (_, image) in images.enumerated() { queue.async { semaphore.wait() // 检查是否已经失败 lock.lock() if hasError { lock.unlock() semaphore.signal() return } lock.unlock() // 压缩图片 guard let imageData = image.jpegData(compressionQuality: 0.5) else { lock.lock() hasError = true lock.unlock() semaphore.signal() DispatchQueue.main.async { failure(YMLocalizedString("error.image_compress_failed")) } return } // 获取图片格式 let format = UIImage.getImageType(withImageData: imageData) ?? "jpeg" // 生成文件名 let uuid = NSString.createUUID() let fileName = "image/\(uuid).\(format)" // 直接使用 QCloud SDK 上传 let request = QCloudCOSXMLUploadObjectRequest() request.bucket = bucket request.object = fileName request.body = imageData as NSData // 监听上传进度(可选) request.sendProcessBlock = { bytesSent, totalBytesSent, totalBytesExpectedToSend in // 单个文件的上传进度(当前不使用) } // 监听上传结果 request.finishBlock = { [weak self] result, error in guard let self = self else { semaphore.signal() return } if let error = error { // 上传失败 lock.lock() if !hasError { hasError = true lock.unlock() semaphore.signal() DispatchQueue.main.async { failure(error.localizedDescription) } } else { lock.unlock() semaphore.signal() } } else if let result = result as? QCloudUploadObjectResult { // 上传成功 lock.lock() if !hasError { uploadedCount += 1 // 解析上传 URL(参考 UploadFile.m line 217-223) let uploadedURL = self.parseUploadURL(result.location, customDomain: customDomain) let imageInfo: [String: Any] = [ "resUrl": uploadedURL, "width": image.size.width, "height": image.size.height, "format": format ] resultList.append(imageInfo) let currentUploaded = uploadedCount lock.unlock() // 进度回调 DispatchQueue.main.async { progress(currentUploaded, total) } // 全部完成 if currentUploaded == total { DispatchQueue.main.async { success(resultList) } } } else { lock.unlock() } semaphore.signal() } else { semaphore.signal() } } // 执行上传 QCloudCOSTransferMangerService.defaultCOSTransferManager().uploadObject(request) } } } /// 解析上传返回的 URL(参考 UploadFile.m line 217-223) /// - Parameters: /// - location: QCloud 返回的原始 URL /// - customDomain: 自定义域名 /// - Returns: 解析后的 URL private func parseUploadURL(_ location: String, customDomain: String) -> String { let components = location.components(separatedBy: ".com/") if components.count == 2 { return "\(customDomain)/\(components[1])" } return location } }