import Foundation import UniformTypeIdentifiers public struct FileUploadResult: Codable, Sendable { public let url: String public let thumbnailUrl: String? public let hash: String public let size: Int64 public let originalName: String? public let mimeType: String? public let ext: String? } public final class FileSDK: @unchecked Sendable { public static let shared = FileSDK() private init() {} public func upload(fileURL: URL, thumbnailData: Data? = nil) async throws -> FileUploadResult { let config = await XuqmSDK.shared.requireConfig() let tokenStore = await XuqmSDK.shared.tokenStore let boundary = "Boundary-\(UUID().uuidString)" let url = SDKEndpoints.apiBaseURL.appendingPathComponent("api/file/upload") var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") if let token = tokenStore?.get() { request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } let body = try createMultipartBody(fileURL: fileURL, boundary: boundary, thumbnailData: thumbnailData) let (data, response) = try await URLSession.shared.upload(for: request, from: body) guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else { throw URLError(.badServerResponse) } let wrapper = try JSONDecoder().decode(ApiResponse.self, from: data) guard let result = wrapper.data else { throw URLError(.cannotDecodeContentData) } return result } private func createMultipartBody(fileURL: URL, boundary: String, thumbnailData: Data?) throws -> Data { var body = Data() let filename = fileURL.lastPathComponent let mimeType: String if #available(iOS 14.0, macOS 11.0, *) { if let uti = UTType(filenameExtension: (fileURL.path as NSString).pathExtension), let preferred = uti.preferredMIMEType { mimeType = preferred } else { mimeType = "application/octet-stream" } } else { mimeType = "application/octet-stream" } body.append("--\(boundary)\r\n") body.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(filename)\"\r\n") body.append("Content-Type: \(mimeType)\r\n\r\n") body.append(try Data(contentsOf: fileURL)) body.append("\r\n") if let thumbnailData { let thumbFilename = "\((filename as NSString).deletingPathExtension)_thumb.jpg" body.append("--\(boundary)\r\n") body.append("Content-Disposition: form-data; name=\"thumbnail\"; filename=\"\(thumbFilename)\"\r\n") body.append("Content-Type: image/jpeg\r\n\r\n") body.append(thumbnailData) body.append("\r\n") } body.append("--\(boundary)--\r\n") return body } } private extension Data { mutating func append(_ string: String) { if let data = string.data(using: .utf8) { append(data) } } }