- 为 Android、Flutter、iOS 和 RN SDK 的 Jenkinsfile 添加模块化版本控制 - 引入版本升级策略选择(major/minor/patch)和自定义版本号功能 - 实现多模块独立版本管理和选择性构建发布 - 更新 iOS SDK Package.swift 以支持独立模块化库 - 修改 iOS SDK podspec 文件以适应新的标签命名约定 - 优化 Jenkins 构建流程以支持按需选择特定模块进行构建和发布 - 修复 iOS 测试中的可选类型转换问题以提高代码健壮性
87 行
3.2 KiB
Swift
87 行
3.2 KiB
Swift
import Foundation
|
|
import XuqmCoreSDK
|
|
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<FileUploadResult>.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)
|
|
}
|
|
}
|
|
}
|