562 lines
20 KiB
Swift
562 lines
20 KiB
Swift
|
|
//
|
|||
|
|
// CloudStorage.swift
|
|||
|
|
// Crush
|
|||
|
|
//
|
|||
|
|
// Created by Leon on 2025/7/21.
|
|||
|
|
//
|
|||
|
|
|
|||
|
|
import AWSS3
|
|||
|
|
import Foundation
|
|||
|
|
import SwiftDate
|
|||
|
|
// MARK: - CloudStorage
|
|||
|
|
|
|||
|
|
class CloudStorage {
|
|||
|
|
static let shared = CloudStorage()
|
|||
|
|
|
|||
|
|
private(set) var uploadAlbumItems: [UploadPhotoM] = []
|
|||
|
|
private var uploadRoleItems: [UploadPhotoM] = []
|
|||
|
|
private var uploadHeadImagesItems: [UploadPhotoM] = []
|
|||
|
|
private var uploadIMImageItems: [UploadPhotoM] = []
|
|||
|
|
private var uploadAudiosItems: [UploadModel] = []
|
|||
|
|
|
|||
|
|
var authAudioData: S3AuthData?
|
|||
|
|
var requestAudioSysTokenData: Date?
|
|||
|
|
var audioTransferUtility: AWSS3TransferUtility?
|
|||
|
|
|
|||
|
|
private init() {
|
|||
|
|
NotificationCenter.default.addObserver(self, selector: #selector(receiveNetStatusChanged(_:)), name: AppNotificationName.networkChanged.notificationName, object: nil)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
deinit {
|
|||
|
|
NotificationCenter.default.removeObserver(self)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func needCheckImage(for bucket: BucketS3Enum) -> Bool {
|
|||
|
|
switch bucket {
|
|||
|
|
case .HEAD_IMAGE: // , .ALBUM, .ROLE
|
|||
|
|
return true
|
|||
|
|
default:
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - Public Methods
|
|||
|
|
|
|||
|
|
func s3BatchAddPhotos(_ photos: [UploadPhotoM], bucket: BucketS3Enum, callback: ((Bool) -> Void)?) {
|
|||
|
|
guard !photos.isEmpty else {
|
|||
|
|
callback?(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var uploadItems: [Any] = []
|
|||
|
|
switch bucket {
|
|||
|
|
case .HEAD_IMAGE:
|
|||
|
|
uploadItems = uploadHeadImagesItems
|
|||
|
|
case .ALBUM:
|
|||
|
|
uploadItems = uploadAlbumItems
|
|||
|
|
case .ROLE:
|
|||
|
|
uploadItems = uploadRoleItems
|
|||
|
|
case .IM_IMG:
|
|||
|
|
uploadItems = uploadIMImageItems
|
|||
|
|
case .SOUND, .SOUND_PATH:
|
|||
|
|
uploadItems = uploadAudiosItems
|
|||
|
|
default:
|
|||
|
|
callback?(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for photo in photos {
|
|||
|
|
photo.isUploading = true
|
|||
|
|
photo.bucketEnum = bucket
|
|||
|
|
if !uploadItems.contains(where: { $0 as? UploadPhotoM === photo }) {
|
|||
|
|
uploadItems.append(photo)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
doS3UploadPhotos(bucket, photos: photos, block: callback)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func s3AddPhotos(_ photos: [UploadPhotoM], bucket: BucketS3Enum) {
|
|||
|
|
guard !photos.isEmpty else { return }
|
|||
|
|
|
|||
|
|
var uploadItems: [Any] = []
|
|||
|
|
switch bucket {
|
|||
|
|
case .HEAD_IMAGE:
|
|||
|
|
uploadItems = uploadHeadImagesItems
|
|||
|
|
case .ALBUM:
|
|||
|
|
uploadItems = uploadAlbumItems
|
|||
|
|
case .ROLE:
|
|||
|
|
uploadItems = uploadRoleItems
|
|||
|
|
case .IM_IMG:
|
|||
|
|
uploadItems = uploadIMImageItems
|
|||
|
|
case .SOUND, .SOUND_PATH:
|
|||
|
|
uploadItems = uploadAudiosItems
|
|||
|
|
default:
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for photo in photos {
|
|||
|
|
photo.isUploading = true
|
|||
|
|
photo.bucketEnum = bucket
|
|||
|
|
if !uploadItems.contains(where: { $0 as? UploadPhotoM === photo }) {
|
|||
|
|
uploadItems.append(photo)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
doS3UploadPhotos(bucket, photos: photos, block: nil)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func s3AddUploadAudio(_ model: UploadModel, callback: ((Bool) -> Void)?) {
|
|||
|
|
guard let fileData = model.fileData, fileData.count > 0 else {
|
|||
|
|
assert(false, "fileData should be valid")
|
|||
|
|
callback?(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
model.isUploading = true
|
|||
|
|
if !uploadAudiosItems.contains(where: { $0 === model }) {
|
|||
|
|
uploadAudiosItems.append(model)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if judgeTokenValid(requestAudioSysTokenData){
|
|||
|
|
guard let authData = authAudioData, let utility = audioTransferUtility else{
|
|||
|
|
callback?(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
dlog("☁️利用之前的token上传Audio: \(model)")
|
|||
|
|
self.s3UploadModel(utility, object: model, auth: authData) { result in
|
|||
|
|
callback?(result)
|
|||
|
|
}
|
|||
|
|
}else{
|
|||
|
|
requestAuthToken(.SOUND_PATH, suffix: .mp3) { [weak self] result, authData, utility in
|
|||
|
|
guard let self = self, !self.uploadAudiosItems.isEmpty, let s3Utility = utility, let s3AuthData = authData else {
|
|||
|
|
callback?(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if result {
|
|||
|
|
self.requestAudioSysTokenData = Date()
|
|||
|
|
self.s3UploadModel(s3Utility, object: model, auth: s3AuthData) { result in
|
|||
|
|
callback?(result)
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
model.isUploading = false
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
model.mm_uploadFailed()
|
|||
|
|
}
|
|||
|
|
callback?(false)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func cancelTasks(for bucket: BucketS3Enum) {
|
|||
|
|
switch bucket {
|
|||
|
|
case .ALBUM:
|
|||
|
|
for photo in uploadAlbumItems {
|
|||
|
|
cancelTask(for: photo)
|
|||
|
|
uploadAlbumItems.removeAll { $0 === photo }
|
|||
|
|
}
|
|||
|
|
case .HEAD_IMAGE:
|
|||
|
|
for photo in uploadHeadImagesItems {
|
|||
|
|
cancelTask(for: photo)
|
|||
|
|
uploadHeadImagesItems.removeAll { $0 === photo }
|
|||
|
|
}
|
|||
|
|
case .ROLE:
|
|||
|
|
for model in uploadRoleItems {
|
|||
|
|
cancelTask(for: model)
|
|||
|
|
uploadRoleItems.removeAll { $0 === model }
|
|||
|
|
}
|
|||
|
|
case .IM_IMG:
|
|||
|
|
for model in uploadIMImageItems {
|
|||
|
|
cancelTask(for: model)
|
|||
|
|
uploadIMImageItems.removeAll { $0 === model }
|
|||
|
|
}
|
|||
|
|
case .SOUND, .SOUND_PATH:
|
|||
|
|
for model in uploadAudiosItems{
|
|||
|
|
cancelTask(for: model)
|
|||
|
|
uploadAudiosItems.removeAll {$0 === model}
|
|||
|
|
}
|
|||
|
|
default:
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - Private Methods
|
|||
|
|
|
|||
|
|
private func doS3UploadPhotos(_ bucket: BucketS3Enum, photos: [UploadPhotoM], block: ((Bool) -> Void)?) {
|
|||
|
|
guard !photos.isEmpty else {
|
|||
|
|
assert(false, "photos count 0")
|
|||
|
|
block?(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let enableGCDGroup = block != nil
|
|||
|
|
var group: DispatchGroup?
|
|||
|
|
var isAllOKInGroup = true
|
|||
|
|
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group = DispatchGroup()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var tempPhotos = photos
|
|||
|
|
// ⚠️
|
|||
|
|
// let checkImageNeedOfBucket = needCheckImage(for: bucket)
|
|||
|
|
|
|||
|
|
for photo in photos {
|
|||
|
|
if let tempString = photo.remoteImageUrlString, tempString.count > 0 {
|
|||
|
|
tempPhotos.removeAll { $0 === photo }
|
|||
|
|
removeObjFromUploadingItems(by: photo)
|
|||
|
|
photo.mm_uploadDone(tempString)
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
guard photo.image != nil || photo.imageData != nil else {
|
|||
|
|
dlog("❌❌❌: 错误路径,图片应不能为空【CloudStorage】")
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group?.enter()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if photo.imageData == nil {
|
|||
|
|
photo.imageData = convertImageToData(photo.image!, suffix: photo.suffixEnum)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let sizeInMB = Float(photo.imageData!.count) / (1000.0 * 1000.0)
|
|||
|
|
if sizeInMB > 10 {
|
|||
|
|
photo.setupPhotoOversizeLimmit()
|
|||
|
|
isAllOKInGroup = false
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
let message = photo.suffixEnum == .gif ? "Photo must be .JPG, .JPEG, .GIF or .PNG and cannot exceed 10MB" : "Photo must be .JPG, .JPEG or .PNG and cannot exceed 10MB"
|
|||
|
|
photo.setupErrorMsg(message)
|
|||
|
|
// Assuming Hud is a utility for showing alerts/toasts
|
|||
|
|
// Unknown: Hud ShowPureTip implementation
|
|||
|
|
dlog(message) // Placeholder for Hud.ShowPureTip
|
|||
|
|
photo.mm_uploadFailed()
|
|||
|
|
}
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group?.leave()
|
|||
|
|
}
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// && checkImageNeedOfBucket
|
|||
|
|
let isCheckImageInCallback = block != nil && photo.isAutoCheckImage
|
|||
|
|
if isCheckImageInCallback {
|
|||
|
|
photo.banCheckImageInDelegateMethod = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let suffixType = photo.imageData != nil ? photo.suffixEnum : .jpeg
|
|||
|
|
|
|||
|
|
requestAuthToken(bucket, suffix: suffixType) { [weak self] result, authData, utility in
|
|||
|
|
guard let self = self, let s3AuthData = authData, let s3Utility = utility else { return }
|
|||
|
|
if result {
|
|||
|
|
self.s3Upload(s3Utility, photoM: photo, authData: s3AuthData) { success in
|
|||
|
|
photo.isUploading = false
|
|||
|
|
if success {
|
|||
|
|
if isCheckImageInCallback {
|
|||
|
|
photo.checkImageOK { result2 in
|
|||
|
|
if !result2 { // 鉴定失败❌
|
|||
|
|
isAllOKInGroup = false
|
|||
|
|
}
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group?.leave()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 不需要鉴黄,直接返回
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group?.leave()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 上传失败❌
|
|||
|
|
isAllOKInGroup = false
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group?.leave()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 获取token失败❌
|
|||
|
|
isAllOKInGroup = false
|
|||
|
|
if enableGCDGroup {
|
|||
|
|
group?.leave()
|
|||
|
|
}
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
photo.mm_uploadFailed()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if enableGCDGroup, let validGroup = group {
|
|||
|
|
// DispatchQueue.main.async(group: group) {
|
|||
|
|
validGroup.notify(queue: .main) {
|
|||
|
|
block?(isAllOKInGroup)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func s3Upload(_ utility: AWSS3TransferUtility, photoM im: UploadPhotoM, authData: S3AuthData, result: @escaping (Bool) -> Void) {
|
|||
|
|
guard !authData.path.isEmpty else {
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
dlog("☁️❌authData.path is empty")
|
|||
|
|
im.mm_uploadFailed()
|
|||
|
|
result(false)
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if LTNetworkManage.ltManage.reachable == false { // Placeholder for [EGApiManager shared].status == .notReachable
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
im.mm_uploadFailed()
|
|||
|
|
result(false)
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
im.s3AuthData = authData
|
|||
|
|
im.utility = utility
|
|||
|
|
|
|||
|
|
let key = authData.path
|
|||
|
|
dlog("☁️uplading key: \(key)")
|
|||
|
|
|
|||
|
|
let expression = AWSS3TransferUtilityUploadExpression()
|
|||
|
|
expression.progressBlock = { _, progress in
|
|||
|
|
dlog("🚀 \(key) taskprogress: \(progress)")
|
|||
|
|
}
|
|||
|
|
expression.setValue("temp=1", forRequestHeader: "x-amz-tagging")
|
|||
|
|
|
|||
|
|
guard let imageData = convertImageToData(im.image!, suffix: im.suffixEnum) ?? im.imageData else { return }
|
|||
|
|
let contentType = im.contentType()
|
|||
|
|
|
|||
|
|
utility.uploadData(imageData, bucket: authData.bucket, key: key, contentType: contentType, expression: expression) { task, error in
|
|||
|
|
self.removeObjFromUploadingItems(by: im)
|
|||
|
|
if let error = error {
|
|||
|
|
dlog("❌S3上传任务失败: \(error.localizedDescription)")
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
im.mm_uploadFailed()
|
|||
|
|
result(false)
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch task.status {
|
|||
|
|
case .completed:
|
|||
|
|
im.remoteFullPath = authData.urlPath
|
|||
|
|
im.remoteImageUrlString = key
|
|||
|
|
dlog("💹 s3 image uploaded full: \(im.remoteFullPath ?? "")")
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
im.mm_uploadDone(key)
|
|||
|
|
result(true)
|
|||
|
|
}
|
|||
|
|
case .error, .cancelled:
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
im.mm_uploadFailed()
|
|||
|
|
result(false)
|
|||
|
|
}
|
|||
|
|
default:
|
|||
|
|
assert(false, "上传异常分支、需要查查原因")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func s3UploadModel(_ utility: AWSS3TransferUtility, object model: UploadModel, auth: S3AuthData, result: @escaping (Bool) -> Void) {
|
|||
|
|
guard !auth.path.isEmpty, let fileData = model.fileData else {
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
model.mm_uploadFailed()
|
|||
|
|
}
|
|||
|
|
result(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Assuming EGApiManager is a network status manager
|
|||
|
|
// Unknown: EGApiManager shared status implementation
|
|||
|
|
if false { // Placeholder for [EGApiManager shared].status == .notReachable
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
model.mm_uploadFailed()
|
|||
|
|
result(false)
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
model.s3AuthData = auth
|
|||
|
|
model.utility = utility
|
|||
|
|
|
|||
|
|
var key = auth.path
|
|||
|
|
/// dev/main/sound/0/*
|
|||
|
|
if key.hasSuffix("/*") {
|
|||
|
|
// 移除最后的 "/*"
|
|||
|
|
key.removeLast(2)
|
|||
|
|
// 拼接时间戳 + .mp3
|
|||
|
|
key += "/\(model.addThisItemTimeStamp).mp3"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var urlPath = auth.urlPath ?? ""
|
|||
|
|
if urlPath.hasSuffix("/*") {
|
|||
|
|
// 移除最后的 "/*"
|
|||
|
|
urlPath.removeLast(2)
|
|||
|
|
// 拼接时间戳 + .mp3
|
|||
|
|
urlPath += "/\(model.addThisItemTimeStamp).mp3"
|
|||
|
|
}
|
|||
|
|
dlog("☁️uplading file key: \(key) full: \(urlPath)")
|
|||
|
|
|
|||
|
|
let expression = AWSS3TransferUtilityUploadExpression()
|
|||
|
|
expression.progressBlock = { _, progress in
|
|||
|
|
dlog("🚀 \(key) file taskprogress: \(progress)")
|
|||
|
|
}
|
|||
|
|
expression.setValue("temp=1", forRequestHeader: "x-amz-tagging")
|
|||
|
|
|
|||
|
|
let contentType = model.contentType()
|
|||
|
|
|
|||
|
|
utility.uploadData(fileData, bucket: auth.bucket, key: key, contentType: contentType, expression: expression) { task, error in
|
|||
|
|
self.uploadAudiosItems.removeAll { $0 === model }
|
|||
|
|
if let error = error {
|
|||
|
|
dlog("❌S3上传任务失败: \(error.localizedDescription)")
|
|||
|
|
result(false)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch task.status {
|
|||
|
|
case .completed:
|
|||
|
|
model.remoteFullPath = urlPath // auth.urlPath
|
|||
|
|
model.remoteImageUrlString = key
|
|||
|
|
dlog("💹 s3 file uploaded full: \(model.remoteFullPath ?? "")")
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
model.mm_uploadDone(key)
|
|||
|
|
result(true)
|
|||
|
|
}
|
|||
|
|
case .error, .cancelled:
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
model.mm_uploadFailed()
|
|||
|
|
result(false)
|
|||
|
|
}
|
|||
|
|
default:
|
|||
|
|
assert(false, "上传异常分支、需要查查原因")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func requestAuthToken(_ bucket: BucketS3Enum, suffix: SuffixS3Enum, callback: @escaping (Bool, S3AuthData?, AWSS3TransferUtility?) -> Void) {
|
|||
|
|
OssProvider.request(.getS3Token(bucketNameEnum: bucket, suffix: suffix), modelType: S3AuthData.self) {[weak self] result in
|
|||
|
|
switch result {
|
|||
|
|
case let .success(success):
|
|||
|
|
if let authData = success {
|
|||
|
|
let utility = self?.generateTransferUtility(bucket, authData: authData)
|
|||
|
|
callback(true, authData, utility)
|
|||
|
|
|
|||
|
|
if bucket == .SOUND_PATH{
|
|||
|
|
self?.authAudioData = success
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
callback(false, nil, nil)
|
|||
|
|
}
|
|||
|
|
case .failure:
|
|||
|
|
callback(false, nil, nil)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func generateTransferUtility(_ bucket: BucketS3Enum, authData: S3AuthData) -> AWSS3TransferUtility? {
|
|||
|
|
guard let accessKeyId = authData.accessKeyId, !accessKeyId.isEmpty,
|
|||
|
|
let accessKeySecret = authData.accessKeySecret, !accessKeySecret.isEmpty,
|
|||
|
|
let securityToken = authData.securityToken, !securityToken.isEmpty else {
|
|||
|
|
assert(false, "invalid token")
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Region(⚠️String) from server
|
|||
|
|
// let region = authData.region
|
|||
|
|
|
|||
|
|
let provider = AWSBasicSessionCredentialsProvider(accessKey: accessKeyId, secretKey: accessKeySecret, sessionToken: securityToken)
|
|||
|
|
// ⚠️(Integer)支持哪些region,需要转化。目前强制 .USWest2
|
|||
|
|
let regionType: AWSRegionType = .USWest2;
|
|||
|
|
guard let config = AWSServiceConfiguration(region: regionType, credentialsProvider: provider) else {
|
|||
|
|
assert(false, "Invalid AWSServiceConfiguration")
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
config.timeoutIntervalForRequest = 30
|
|||
|
|
config.maxRetryCount = 1
|
|||
|
|
|
|||
|
|
let key = authData.fileName
|
|||
|
|
AWSS3TransferUtility.remove(forKey: key) // AWSS3TransferUtility.removeS3TransferUtility(forKey: key)
|
|||
|
|
AWSS3TransferUtility.register(with: config, forKey: key)
|
|||
|
|
|
|||
|
|
let utility = AWSS3TransferUtility.s3TransferUtility(forKey: key) // AWSS3TransferUtility(forKey: key)
|
|||
|
|
if utility == nil {
|
|||
|
|
assert(false, "invalid utility")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch bucket{
|
|||
|
|
case .SOUND_PATH:
|
|||
|
|
audioTransferUtility = utility
|
|||
|
|
default:
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utility
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func judgeTokenValid(_ lastRequestDate: Date?) -> Bool {
|
|||
|
|
guard let date = lastRequestDate else {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
let expireDate = date + 59.minutes
|
|||
|
|
|
|||
|
|
if expireDate.milliStamp > Date().milliStamp {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func removeObjFromUploadingItems(by obj: UploadPhotoM) {
|
|||
|
|
uploadAlbumItems.removeAll { $0 === obj }
|
|||
|
|
uploadHeadImagesItems.removeAll { $0 === obj }
|
|||
|
|
uploadRoleItems.removeAll { $0 === obj }
|
|||
|
|
uploadAudiosItems.removeAll { $0 === obj }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private func cancelTask<T: UploadEntity>(for item: T) {
|
|||
|
|
// Unknown: AWSTask and AWSS3TransferUtilityUploadTask implementation
|
|||
|
|
if let utility = item.utility {
|
|||
|
|
// Placeholder for task cancellation
|
|||
|
|
dlog("🈲🈲, upload task canceled : \(utility)")
|
|||
|
|
DispatchQueue.main.async {
|
|||
|
|
item.mm_uploadFailed()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - Notification Handling
|
|||
|
|
|
|||
|
|
@objc private func receiveNetStatusChanged(_ notification: Notification) {
|
|||
|
|
let status = LTNetworkManage.ltManage.status
|
|||
|
|
if status == .notReachable {
|
|||
|
|
cancelTasks(for: .ALBUM)
|
|||
|
|
cancelTasks(for: .HEAD_IMAGE)
|
|||
|
|
cancelTasks(for: .ROLE)
|
|||
|
|
cancelTasks(for: .SOUND)
|
|||
|
|
cancelTasks(for: .IM_IMG)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// MARK: - Helper Methods
|
|||
|
|
|
|||
|
|
private func convertImageToData(_ image: UIImage, suffix: SuffixS3Enum) -> Data? {
|
|||
|
|
// Unknown: SDWebImage or UIImage conversion implementation
|
|||
|
|
switch suffix {
|
|||
|
|
case .gif:
|
|||
|
|
assert(false, "imageData应该不为空")
|
|||
|
|
return nil
|
|||
|
|
// return image.sd_imageData(as: .gif) // Placeholder
|
|||
|
|
case .png:
|
|||
|
|
return image.pngData()
|
|||
|
|
default:
|
|||
|
|
return image.jpegData(compressionQuality: 0.8)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|