diff --git a/wake/View/Components/Upload/Avatar.swift b/wake/View/Components/Upload/Avatar.swift index c371cc6..0295e97 100644 --- a/wake/View/Components/Upload/Avatar.swift +++ b/wake/View/Components/Upload/Avatar.swift @@ -5,6 +5,7 @@ import PhotosUI <<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD // MARK: - Photo Picker ======= /// 上传管理器,处理图片上传 @@ -205,6 +206,8 @@ public struct UploadResults { } } +======= +>>>>>>> 5edee64 (feat: 文件上传成功) // MARK: - Photo Picker >>>>>>> 8e641fd (feat: 上传进度) @@ -213,6 +216,7 @@ struct PhotoPicker: UIViewControllerRepresentable { <<<<<<< HEAD ======= // MARK: - Properties +<<<<<<< HEAD <<<<<<< HEAD /// 绑定的已选图片数组,用于存储用户选择的图片 @@ -260,14 +264,29 @@ struct PhotoPicker: UIViewControllerRepresentable { ======= var onImageUploaded: ((Result) -> Void)? var onUploadProgress: ((UploadProgress) -> Void)? +======= + + @Binding var selectedImages: [UIImage] + let selectionLimit: Int + let filter: PHPickerFilter + var onImageUploaded: ((Result) -> Void)? + var onUploadProgress: ((ImageUploadService.UploadProgress) -> Void)? + @Environment(\.presentationMode) private var presentationMode +>>>>>>> 5edee64 (feat: 文件上传成功) // MARK: - Initialization + init(selectedImages: Binding<[UIImage]>, selectionLimit: Int = 1, filter: PHPickerFilter = .images, +<<<<<<< HEAD onImageUploaded: ((Result) -> Void)? = nil, onUploadProgress: ((UploadProgress) -> Void)? = nil) { >>>>>>> 8e641fd (feat: 上传进度) +======= + onImageUploaded: ((Result) -> Void)? = nil, + onUploadProgress: ((ImageUploadService.UploadProgress) -> Void)? = nil) { +>>>>>>> 5edee64 (feat: 文件上传成功) self._selectedImages = selectedImages self.selectionLimit = selectionLimit self.filter = filter @@ -280,6 +299,7 @@ struct PhotoPicker: UIViewControllerRepresentable { } // MARK: - UIViewControllerRepresentable +<<<<<<< HEAD <<<<<<< HEAD // MARK: - UIViewControllerRepresentable 协议方法 @@ -292,6 +312,9 @@ struct PhotoPicker: UIViewControllerRepresentable { /// 创建并返回配置好的PHPickerViewController ======= >>>>>>> 8e641fd (feat: 上传进度) +======= + +>>>>>>> 5edee64 (feat: 文件上传成功) func makeUIViewController(context: Context) -> PHPickerViewController { var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) configuration.filter = filter @@ -311,6 +334,7 @@ struct PhotoPicker: UIViewControllerRepresentable { // MARK: - Coordinator <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD // MARK: - 协调器类 @@ -329,15 +353,27 @@ struct PhotoPicker: UIViewControllerRepresentable { ======= >>>>>>> 8e641fd (feat: 上传进度) private let uploader = ImageUploaderGetID() +======= + + class Coordinator: NSObject, PHPickerViewControllerDelegate { + let parent: PhotoPicker + private let uploadService = ImageUploadService.shared +>>>>>>> 5edee64 (feat: 文件上传成功) init(_ parent: PhotoPicker) { self.parent = parent } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + guard !results.isEmpty else { + parent.presentationMode.wrappedValue.dismiss() + return + } + parent.selectedImages.removeAll() let group = DispatchGroup() <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD var loadedImages: [Int: UIImage] = [:] var uploadResults: [Int: ImageUploadService.UploadResults] = [:] @@ -355,11 +391,17 @@ struct PhotoPicker: UIViewControllerRepresentable { var uploadResults: [Int: (original: ImageUploaderGetID.UploadResult?, compressed: ImageUploaderGetID.UploadResult?)] = [:] >>>>>>> 8e641fd (feat: 上传进度) +======= + var loadedImages: [Int: UIImage] = [:] + var uploadResults: [Int: ImageUploadService.UploadResults] = [:] + var lastError: Error? +>>>>>>> 5edee64 (feat: 文件上传成功) for (index, result) in results.enumerated() { group.enter() if result.itemProvider.canLoadObject(ofClass: UIImage.self) { +<<<<<<< HEAD result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (image, error) in <<<<<<< HEAD if let image = image as? UIImage { @@ -370,12 +412,24 @@ struct PhotoPicker: UIViewControllerRepresentable { loadedImages[index] = image ======= guard let self = self, let image = image as? UIImage else { +======= + result.itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in + if let error = error { + lastError = error + group.leave() + return + } + + guard let image = image as? UIImage else { + lastError = NSError(domain: "com.wake.upload", code: -2, userInfo: [NSLocalizedDescriptionKey: "Failed to load image"]) +>>>>>>> 5edee64 (feat: 文件上传成功) group.leave() return } loadedImages[index] = image +<<<<<<< HEAD guard let compressedImage = image.jpegData(compressionQuality: 0.5).flatMap(UIImage.init(data:)) else { group.leave() return @@ -426,77 +480,48 @@ struct PhotoPicker: UIViewControllerRepresentable { } ======= self.uploader.uploadImage( +======= + // Upload the image + self.uploadService.uploadOriginalAndCompressedImage( +>>>>>>> 5edee64 (feat: 文件上传成功) image, - progress: { [weak self] progress in - let progressInfo = UploadProgress( - current: Int(progress * 100), - total: 100, - progress: progress, - isOriginal: true - ) + compressionQuality: 0.5, + progress: { progress in DispatchQueue.main.async { - self?.parent.onUploadProgress?(progressInfo) + self.parent.onUploadProgress?(progress) } - print("📤 原图上传进度: \(Int(progress * 100))%") }, +<<<<<<< HEAD completion: { [weak self] originalResult in guard let self = self else { group.leave() return >>>>>>> 8e641fd (feat: 上传进度) } +======= + completion: { result in + defer { group.leave() } +>>>>>>> 5edee64 (feat: 文件上传成功) - switch originalResult { - case .success(let originalUploadResult): - self.uploader.uploadImage( - compressedImage, - progress: { [weak self] progress in - let progressInfo = UploadProgress( - current: Int(progress * 100), - total: 100, - progress: progress, - isOriginal: false - ) - DispatchQueue.main.async { - self?.parent.onUploadProgress?(progressInfo) - } - print("📊 压缩图上传进度: \(Int(progress * 100))%") - }, - completion: { compressedResult in - defer { group.leave() } - - switch compressedResult { - case .success(let compressedUploadResult): - uploadResults[index] = (originalUploadResult, compressedUploadResult) - print("✅ 原图和压缩图上传成功!") - print("📂 原图信息:") - print(" - 文件ID: \(originalUploadResult.fileId)") - print(" - 文件大小: \(originalUploadResult.fileSize) 字节") - print("📦 压缩图信息:") - print(" - 文件ID: \(compressedUploadResult.fileId)") - print(" - 文件大小: \(compressedUploadResult.fileSize) 字节") - - MaterialService.shared.uploadMaterialInfo( - fileId: originalUploadResult.fileId, - previewFileId: compressedUploadResult.fileId - ) { success, errorMessage in - if success { - print("✅ 文件信息上传成功 素材上传成功!!!!!") - } else if let errorMessage = errorMessage { - print("❌ 文件信息上传失败: \(errorMessage)") - } - } - - case .failure(let error): - print("❌ 压缩图上传失败: \(error.localizedDescription)") - uploadResults[index] = (originalUploadResult, nil) - } + switch result { + case .success(let results): + uploadResults[index] = results + + // Upload file info to backend + MaterialService.shared.uploadMaterialInfo( + fileId: results.original.fileId, + previewFileId: results.compressed.fileId + ) { success, errorMessage in + if success { + print("✅ 文件信息上传成功") + } else if let errorMessage = errorMessage { + print("❌ 文件信息上传失败: \(errorMessage)") } - ) + } case .failure(let error): - print("❌ 原图上传失败: \(error.localizedDescription)") - group.leave() + lastError = error + print("❌ 图片上传失败: \(error.localizedDescription)") } } ) @@ -535,28 +560,33 @@ struct PhotoPicker: UIViewControllerRepresentable { group.notify(queue: .main) { [weak self] in guard let self = self else { return } - let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value } - self.parent.selectedImages.append(contentsOf: sortedImages) - - if let firstResult = uploadResults.first?.value, - let original = firstResult.original, - let compressed = firstResult.compressed { - let results = UploadResults(original: original, compressed: compressed) - self.parent.onImageUploaded?(.success(results)) + if let error = lastError { + self.parent.onImageUploaded?(.failure(error)) } else { - self.parent.onImageUploaded?(.failure(NSError( - domain: "com.wake.upload", - code: -1, - userInfo: [NSLocalizedDescriptionKey: "上传过程中出现错误"] - ))) + let sortedImages = loadedImages.sorted { $0.key < $1.key }.map { $0.value } + self.parent.selectedImages.append(contentsOf: sortedImages) + + if let firstResult = uploadResults.first?.value { + self.parent.onImageUploaded?(.success(firstResult)) + } else { + self.parent.onImageUploaded?(.failure(NSError( + domain: "com.wake.upload", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "上传过程中出现错误"] + ))) + } } +<<<<<<< HEAD <<<<<<< HEAD // 5. 关闭图片选择器 >>>>>>> 5611df8 (feat: 素材上传成) ======= >>>>>>> 8e641fd (feat: 上传进度) picker.dismiss(animated: true) +======= + self.parent.presentationMode.wrappedValue.dismiss() +>>>>>>> 5edee64 (feat: 文件上传成功) } } } @@ -579,6 +609,7 @@ struct PhotoPicker: UIViewControllerRepresentable { struct AvatarUploader: View { @Binding var selectedImage: UIImage? let size: CGFloat +<<<<<<< HEAD <<<<<<< HEAD var onUploadComplete: ((Result) -> Void)? @@ -586,6 +617,9 @@ struct AvatarUploader: View { ======= >>>>>>> 8e641fd (feat: 上传进度) var onUploadComplete: ((Result) -> Void)? +======= + var onUploadComplete: ((Result) -> Void)? +>>>>>>> 5edee64 (feat: 文件上传成功) @State private var isImagePickerPresented = false @@ -662,7 +696,7 @@ struct AvatarUploader: View { onUploadComplete?(result) }, onUploadProgress: { progress in - print("上传进度:\(progress.current)/\(progress.total),进度:\(progress.progress * 100)%") + print("上传进度:\(progress.current)/\(progress.total),进度:\(Int(progress.progress * 100))%") } ) } diff --git a/wake/View/Components/Upload/ImageUploader.swift b/wake/View/Components/Upload/ImageUploader.swift deleted file mode 100644 index e69de29..0000000 diff --git a/wake/View/Components/Upload/ImageUploaderGetID.swift b/wake/View/Components/Upload/ImageUploaderGetID.swift index 15f766d..11a4266 100644 --- a/wake/View/Components/Upload/ImageUploaderGetID.swift +++ b/wake/View/Components/Upload/ImageUploaderGetID.swift @@ -7,11 +7,15 @@ public class ImageUploaderGetID: ObservableObject { // MARK: - 类型定义 /// 上传结果 +<<<<<<< HEAD <<<<<<< HEAD public struct UploadResult: Codable { ======= public struct UploadResult { >>>>>>> a207b78 (feat: 确认上传) +======= + public struct UploadResult: Codable { +>>>>>>> 5edee64 (feat: 文件上传成功) public let fileUrl: String public let fileName: String public let fileSize: Int @@ -33,10 +37,14 @@ public class ImageUploaderGetID: ObservableObject { case invalidResponse case uploadFailed(Error?) case invalidFileId +<<<<<<< HEAD <<<<<<< HEAD case invalidResponseData ======= >>>>>>> a207b78 (feat: 确认上传) +======= + case invalidResponseData +>>>>>>> 5edee64 (feat: 文件上传成功) public var errorDescription: String? { switch self { @@ -52,11 +60,16 @@ public class ImageUploaderGetID: ObservableObject { return "上传失败: \(error?.localizedDescription ?? "未知错误")" case .invalidFileId: return "无效的文件ID" +<<<<<<< HEAD <<<<<<< HEAD case .invalidResponseData: return "无效的响应数据" ======= >>>>>>> a207b78 (feat: 确认上传) +======= + case .invalidResponseData: + return "无效的响应数据" +>>>>>>> 5edee64 (feat: 文件上传成功) } } } @@ -237,6 +250,7 @@ public class ImageUploaderGetID: ObservableObject { /// 确认上传 private func confirmUpload(fileId: String, fileName: String, fileSize: Int, completion: @escaping (Result) -> Void) { +<<<<<<< HEAD <<<<<<< HEAD let endpoint = "\(apiConfig.baseURL)/file/confirm-upload" guard let url = URL(string: endpoint) else { @@ -244,6 +258,10 @@ public class ImageUploaderGetID: ObservableObject { let urlString = "\(apiConfig.baseURL)/file/confirm-upload" guard let url = URL(string: urlString) else { >>>>>>> a207b78 (feat: 确认上传) +======= + let endpoint = "\(apiConfig.baseURL)/file/confirm-upload" + guard let url = URL(string: endpoint) else { +>>>>>>> 5edee64 (feat: 文件上传成功) completion(.failure(UploadError.invalidURL)) return } @@ -253,11 +271,15 @@ public class ImageUploaderGetID: ObservableObject { request.allHTTPHeaderFields = apiConfig.authHeaders <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 5edee64 (feat: 文件上传成功) let body: [String: Any] = [ "file_id": fileId, "file_name": fileName, "file_size": fileSize ] +<<<<<<< HEAD do { request.httpBody = try JSONSerialization.data(withJSONObject: body) @@ -266,26 +288,40 @@ public class ImageUploaderGetID: ObservableObject { print("❌ 序列化确认上传参数失败: \(error.localizedDescription)") ======= let requestBody: [String: Any] = ["file_id": fileId] +======= +>>>>>>> 5edee64 (feat: 文件上传成功) do { - request.httpBody = try JSONSerialization.data(withJSONObject: requestBody) + request.httpBody = try JSONSerialization.data(withJSONObject: body) + print("📤 确认上传请求,fileId: \(fileId), 文件名: \(fileName)") } catch { +<<<<<<< HEAD >>>>>>> a207b78 (feat: 确认上传) +======= + print("❌ 序列化确认上传参数失败: \(error.localizedDescription)") +>>>>>>> 5edee64 (feat: 文件上传成功) completion(.failure(error)) return } let task = session.dataTask(with: request) { data, response, error in if let error = error { +<<<<<<< HEAD <<<<<<< HEAD print("❌ 确认上传请求失败: \(error.localizedDescription)") ======= >>>>>>> a207b78 (feat: 确认上传) +======= + print("❌ 确认上传请求失败: \(error.localizedDescription)") +>>>>>>> 5edee64 (feat: 文件上传成功) completion(.failure(UploadError.uploadFailed(error))) return } <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 5edee64 (feat: 文件上传成功) guard let httpResponse = response as? HTTPURLResponse else { print("❌ 无效的服务器响应") completion(.failure(UploadError.invalidResponse)) @@ -297,11 +333,14 @@ public class ImageUploaderGetID: ObservableObject { let errorMessage = "确认上传失败,状态码: \(statusCode)" print("❌ \(errorMessage)") completion(.failure(UploadError.serverError(errorMessage))) +<<<<<<< HEAD ======= guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { completion(.failure(UploadError.serverError("确认上传失败,状态码: \((response as? HTTPURLResponse)?.statusCode ?? -1)"))) >>>>>>> a207b78 (feat: 确认上传) +======= +>>>>>>> 5edee64 (feat: 文件上传成功) return } @@ -352,6 +391,9 @@ public class ImageUploaderGetID: ObservableObject { } <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> 5edee64 (feat: 文件上传成功) guard let httpResponse = response as? HTTPURLResponse else { completion(.failure(UploadError.invalidResponse)) return @@ -359,11 +401,14 @@ public class ImageUploaderGetID: ObservableObject { guard httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 else { let statusCode = httpResponse.statusCode +<<<<<<< HEAD ======= guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1 >>>>>>> 8e641fd (feat: 上传进度) +======= +>>>>>>> 5edee64 (feat: 文件上传成功) completion(.failure(UploadError.serverError("上传失败,状态码: \(statusCode)"))) return } @@ -384,11 +429,15 @@ public class ImageUploaderGetID: ObservableObject { task?.progress.cancel() } } else { +<<<<<<< HEAD <<<<<<< HEAD // iOS 11 以下版本使用通知 ======= // Fallback for earlier iOS versions >>>>>>> 8e641fd (feat: 上传进度) +======= + // iOS 11 以下版本使用通知 +>>>>>>> 5edee64 (feat: 文件上传成功) var lastProgress: Double = 0 let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in let bytesSent = task.countOfBytesSent