Swift中获取图片大小并优化:YPImagePicker图片压缩问题
Swift中YPImagePicker图片处理:准确获取大小+压缩至1MB以内的最优方案
问题根源
你之前的代码计算的是图片转成JPEG后的大小,而相册显示的是原始图片(比如HEIC格式)的文件大小,两种格式压缩逻辑不同,导致数值不符。要解决问题,得先获取原始文件的准确大小,再做针对性压缩。
一、获取图片原始大小的正确方式
根据图片来源(相册/相机),用不同的方法获取原始文件大小:
1. 相册选中的图片(PHAsset)
通过YPImagePicker返回的YPPhoto.asset属性,读取PHAsset的原始资源大小:
func getAssetOriginalSize(asset: PHAsset, completion: @escaping (Double?) -> Void) { let resources = PHAssetResource.assetResources(for: asset) guard let targetResource = resources.first else { completion(nil) return } // 读取原始文件大小(单位:MB) let fileSizeBytes = targetResource.value(forKey: "fileSize") as? Int64 let fileSizeMB = fileSizeBytes.flatMap { Double($0) / 1024 / 1024 } completion(fileSizeMB) }
2. 相机拍摄的图片
YPImagePicker拍摄的图片会生成临时本地文件,通过YPPhoto.imageURL读取文件大小:
func getLocalFileSize(url: URL) -> Double? { do { let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path) let fileSizeBytes = fileAttributes[.size] as? Int64 return fileSizeBytes.flatMap { Double($0) / 1024 / 1024 } } catch { return nil } }
二、图片压缩至1MB以内的最优逻辑
优先通过降低JPEG压缩质量来缩小体积,若压缩到最低质量仍超标,再缩小图片尺寸,平衡画质和大小:
func compressImageToMax1MB(_ image: UIImage) -> Data? { let maxSizeBytes = 1 * 1024 * 1024 // 1MB对应的字节数 var compressionQuality: CGFloat = 1.0 guard var compressedData = image.jpegData(compressionQuality: compressionQuality) else { return nil } // 渐进式降低压缩质量,直到符合大小要求或质量低于0.1 while Double(compressedData.count) > maxSizeBytes && compressionQuality > 0.1 { compressionQuality -= 0.1 if let newData = image.jpegData(compressionQuality: compressionQuality) { compressedData = newData } else { break } } // 若压缩质量到0.1仍超标,缩小图片尺寸 if Double(compressedData.count) > maxSizeBytes { let scaleFactor = sqrt(maxSizeBytes / Double(compressedData.count)) let newSize = CGSize( width: image.size.width * scaleFactor, height: image.size.height * scaleFactor ) UIGraphicsBeginImageContextWithOptions(newSize, false, image.scale) image.draw(in: CGRect(origin: .zero, size: newSize)) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() compressedData = scaledImage?.jpegData(compressionQuality: 0.8) ?? compressedData } return compressedData }
三、整合到YPImagePicker的完整流程
在图片选择回调中,结合大小判断和压缩逻辑,处理最多5张图片:
// 配置YPImagePicker(限制最多5张图片) var config = YPImagePickerConfiguration() config.screens = [.library, .photo] config.library.maxNumberOfItems = 5 let picker = YPImagePicker(configuration: config) picker.didFinishPicking { [weak self] items, _ in guard let self = self else { return } // 遍历选中的图片(最多5张) for item in items { guard case .photo(let photo) = item else { continue } // 区分相册/相机图片,获取原始大小 if let asset = photo.asset { self.getAssetOriginalSize(asset: asset) { mbSize in guard let size = mbSize else { return } if size > 1.0 { // 超过1MB,压缩后发送 if let compressedData = self.compressImageToMax1MB(photo.image) { // 调用REST API发送compressedData到后端 print("压缩后大小:\(Double(compressedData.count)/1024/1024) MB") } } else { // 直接发送原始格式数据(若后端支持HEIC,可读取asset原始数据;否则转JPEG) if let imageData = photo.image.jpegData(compressionQuality: 1.0) { // 调用REST API发送imageData } } } } else if let imageURL = photo.imageURL { if let mbSize = self.getLocalFileSize(url: imageURL) { if mbSize > 1.0 { if let compressedData = self.compressImageToMax1MB(photo.image) { // 发送压缩后的数据 } } else { // 读取原始文件数据发送 do { let originalData = try Data(contentsOf: imageURL) // 调用REST API发送originalData } catch { print("读取原始图片失败:\(error.localizedDescription)") } } } } } picker.dismiss(animated: true) } present(picker, animated: true)
注意事项
- 若后端支持HEIC格式,建议直接发送原始HEIC数据(通过PHAsset读取原始资源或读取相机拍摄的本地文件),比JPEG更省体积且画质更好。
- 压缩时的质量阈值(0.1)和缩放后的质量(0.8)可根据你的需求调整,平衡画质和大小。
内容的提问来源于stack exchange,提问作者Prabhu
相关产品推荐
相关产品推荐

