// // UploadModels.swift // Crush // // Created by Leon on 2025/7/21. // import Foundation import AWSS3 // MARK: - Protocols protocol UploadPhotoCallBackProtocol { func mm_uploadProgress(_ progress: Float) func mm_uploadFailed() func mm_uploadDone(_ remoteImageUrlString: String) } protocol UploadPhotoDelegate: AnyObject { // func uploadFail() // func uploadDone() func uploadDoneWith(_ photo: UploadPhotoM) func uploadFailWith(_ photo: UploadPhotoM) func uploadProgress(_ progress: Float) func imageCheck(_ photoM: UploadPhotoM, result: Bool) // func imageIdentyReal(_ photo: UploadPhotoM, result: Bool) // func imageIdentifyChild(_ photo: UploadPhotoM, result: Bool) // func imageFullCheck(_ photo: UploadPhotoM, nfswCheck: Bool, realPersonCheck: Bool) // func imageFullCheck(_ photo: UploadPhotoM, nfswCheck: Bool, childCheck: Bool) } protocol UploadModelDelegate: AnyObject { func uploadFail() func uploadDone() func uploadDoneWith(_ photo: UploadModel) func uploadFailWith(_ photo: UploadModel) func uploadProgress(_ progress: Float) } // MARK: - UploadEntity class UploadEntity: NSObject, UploadPhotoCallBackProtocol { func mm_uploadProgress(_ progress: Float) { } func mm_uploadFailed() { } func mm_uploadDone(_ remoteImageUrlString: String) { } weak var utility: AWSS3TransferUtility? var addThisItemTimeStamp: Int = 0 var uploadProgress: Float = 0.0 var isFailed: Bool = false var isDone: Bool = false var isUploading: Bool = false } // MARK: - UploadModel class UploadModel: UploadEntity { var fileData: Data? var s3AuthData: S3AuthData? /// 一般是不含域名的部分链接 var remoteImageUrlString: String? var remoteFullPath: String? weak var delegate: UploadModelDelegate? func suffix() -> String { return ".mp3" } func contentType() -> String { return "audio/mp3" } func releaseS3Utility() { if let utility = utility, let _ = s3AuthData?.fileName { //#warning("Confirm") //AWSS3TransferUtility.removeS3TransferUtility(forKey: fileName) utility.cancelAll() } } // MARK: - Setter Overrides override var uploadProgress: Float { get { super.uploadProgress } set { super.uploadProgress = newValue delegate?.uploadProgress(newValue) } } override var isDone: Bool { get { super.isDone } set { super.isDone = newValue delegate?.uploadDoneWith(self) delegate?.uploadDone() } } override var isFailed: Bool { get { super.isFailed } set { super.isFailed = newValue delegate?.uploadFailWith(self) delegate?.uploadFail() } } // MARK: - UploadPhotoCallBackProtocol override func mm_uploadProgress(_ progress: Float) { self.uploadProgress = progress } override func mm_uploadFailed() { releaseS3Utility() isFailed = true isUploading = false } override func mm_uploadDone(_ remoteImageUrlString: String) { releaseS3Utility() self.remoteImageUrlString = remoteImageUrlString isDone = true isUploading = false } } // MARK: - UploadPhotoM class UploadPhotoM: UploadEntity { weak var delegate: UploadPhotoDelegate? // MARK: Data var image: UIImage?{ didSet{ if image?.size.width ?? 0 > 0 && image?.size.height ?? 0 > 0{ imageSize = image!.pixelSize } } } var imageData: Data? var bucketEnum: BucketS3Enum = .UNKNOW var s3AuthData: S3AuthData? /// 一般是不含域名的部分链接 var remoteImageUrlString: String? /// 文件全路径、含域名 var remoteFullPath: String? var imageSize: CGSize = .zero var suffixEnum: SuffixS3Enum = .jpeg // MARK: Config /// -- Config: Default true: 默认需要鉴黄 var isAutoCheckImage: Bool = true var isHideCheckToast: Bool = false // MARK: State private(set) var imageCheckIsViolation: Bool = false /// Default false private(set) var imageChecked: Bool = false private(set) var isOverSizeLimmit: Bool = false private(set) var errorMsg: String? var imageFrame: CGRect = .zero var isEditMode: Bool = false // MARK: Inner methods /// true: 禁止在delegate(UploadPhotoDelegate)回调中鉴黄。 在CloudStorage中block判断和鉴黄。自动管理,上传时不需要设置 var banCheckImageInDelegateMethod: Bool = false override init() { super.init() suffixEnum = .jpeg } // MARK: - Public Methods func getFullUrlString() -> String { if let fullUrl = remoteFullPath, fullUrl.count > 0 { return fullUrl }else if let remoteImageFull = remoteImageUrlString{ return remoteImageFull // .fullUrlFromRelativePath // ⚠️这个路径不是全路径,需要拼接一个域名,暂时不会走到此分支! } return "" } func setupValidImageUrl(url: String?){ guard let imgUrl = url else{return} remoteFullPath = imgUrl isDone = true imageChecked = true addThisItemTimeStamp = Date().timeStamp } /// block返回 false,表示不是黄图 func checkImageOK(_ block: ((Bool) -> Void)? = nil) { guard let tempPath = remoteImageUrlString, tempPath.isNotBlank else { assert(false) block?(false) return } OssProvider.request(.nsfwCheck(fileFullPath: tempPath, s3BucketEnum: bucketEnum.rawValue), modelType: Bool.self) {[weak self] result in guard let `self` = self else { return } self.isUploading = false switch result { case let .success(checkResult): self.imageChecked = true //self.realateUrlAdpatEPalAddSlash(add: true) let nsfw = checkResult ?? true let imgOk = !nsfw self.delegate?.imageCheck(self, result: imgOk) block?(imgOk) case let .failure(error): switch error { case let .serviceError(code, msg): if code == .imageCheckFailed{ self.imageChecked = true self.imageCheckIsViolation = true self.errorMsg = msg self.delegate?.imageCheck(self, result: false) }else{ self.imageCheckIsViolation = false self.isFailed = true self.isDone = false self.delegate?.imageCheck(self, result: false) } default: break } block?(false) } } } func contentType() -> String { switch suffixEnum { case .gif: return "image/gif" case .png: return "image/png" default: return "image/jpeg" } } func releaseS3Utility() { if let utility = utility { //AWSS3TransferUtility.removeS3TransferUtility(forKey: fileName) utility.cancelAll() } } func setupPhotoOversizeLimmit() { isOverSizeLimmit = true } func setupErrorMsg(_ msg: String) { errorMsg = msg } // MARK: - Setter Overrides override var uploadProgress: Float { get { super.uploadProgress } set { super.uploadProgress = newValue delegate?.uploadProgress(newValue) } } override var isDone: Bool { get { super.isDone } set { super.isDone = newValue delegate?.uploadDoneWith(self) // delegate?.uploadDone() } } override var isFailed: Bool { get { super.isFailed } set { super.isFailed = newValue delegate?.uploadFailWith(self) // delegate?.uploadFail() } } // MARK: - UploadPhotoCallBackProtocol override func mm_uploadProgress(_ progress: Float) { self.uploadProgress = progress } override func mm_uploadFailed() { releaseS3Utility() isFailed = true isUploading = false } override func mm_uploadDone(_ remoteImageUrlString: String) { releaseS3Utility() self.remoteImageUrlString = remoteImageUrlString isDone = true if isAutoCheckImage { if !banCheckImageInDelegateMethod { checkImageOK(nil) } } else { isUploading = false realateUrlAdpatEPalAddSlash(add: true) } } // MARK: - Helper @discardableResult private func realateUrlAdpatEPalAddSlash(add: Bool) -> String { var url = remoteImageUrlString ?? "" if add { if !url.isEmpty && !url.hasPrefix("/") { url = "/\(url)" } } else { if !url.isEmpty && url.hasPrefix("/") { url.removeFirst() } } remoteImageUrlString = url return url } }