You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不丢失元数据/EXIF的情况下压缩图片数据(SwiftUI)

解决SwiftUI图片压缩时保留EXIF元数据的问题

核心方案:使用ImageIO框架直接处理原始图片数据

UIImage初始化时会自动丢弃EXIF等元数据,因此必须绕开它,直接用ImageIO框架操作原始图片数据流,实现压缩同时完整保留元数据。

具体实现步骤

  • 从原始图片Data中提取完整元数据(包括EXIF)
  • 基于原始元数据进行图片压缩,通过动态调整压缩质量确保文件大小不超过1MB

完整代码实现

import ImageIO
import MobileCoreServices

func compressImageWithMetadata(originalData: Data, targetMaxSize: Int = 1024 * 1024) -> Data? {
    // 创建图片源
    guard let source = CGImageSourceCreateWithData(originalData as CFData, nil) else {
        return nil
    }
    
    // 获取原始图片的元数据(含EXIF)
    let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, sourceOptions) as? [CFString: Any] else {
        return nil
    }
    
    // 创建内存输出流
    let mutableData = NSMutableData()
    guard let destination = CGImageDestinationCreateWithData(mutableData, kUTTypeJPEG, 1, nil) else {
        return nil
    }
    
    // 从高质量开始循环尝试压缩,直到满足大小要求
    var compressionQuality: CGFloat = 0.9
    let qualityStep: CGFloat = 0.1
    var resultData: Data? = nil
    
    while compressionQuality >= 0.1 {
        mutableData.length = 0 // 重置输出流
        
        // 配置压缩选项:保留元数据+当前压缩质量
        let destOptions: [CFString: Any] = [
            kCGImageDestinationLossyCompressionQuality: compressionQuality,
            kCGImageDestinationMetadata: metadata
        ]
        
        // 写入压缩后的图片
        CGImageDestinationAddImageFromSource(destination, source, 0, destOptions as CFDictionary)
        guard CGImageDestinationFinalize(destination) else {
            compressionQuality -= qualityStep
            continue
        }
        
        // 检查是否符合大小要求
        if mutableData.length <= targetMaxSize {
            resultData = mutableData as Data
            break
        }
        
        compressionQuality -= qualityStep
    }
    
    return resultData
}

结合SwiftUI PhotosPicker的使用示例

struct PhotoUploadView: View {
    @State private var selectedPhoto: PhotosPickerItem?
    @State private var compressedData: Data?
    
    var body: some View {
        PhotosPicker(selection: $selectedPhoto, matching: .images) {
            Text("选择相册照片")
        }
        .onChange(of: selectedPhoto) { _, newItem in
            Task {
                if let rawData = try? await newItem?.loadTransferable(type: Data.self) {
                    // 压缩图片并保留元数据
                    compressedData = compressImageWithMetadata(originalData: rawData)
                    // 此处将compressedData上传至服务器
                }
            }
        }
    }
}

补充说明

  • 该代码默认针对JPEG格式压缩,如需处理HEIC等格式,只需将kUTTypeJPEG替换为对应UTI(如kUTTypeHEIC)
  • 循环调整压缩质量是为了确保最终文件严格低于1MB,你可以根据需求调整质量步长或终止条件
  • 若你能获取到PHAsset实例,也可直接用PHImageManager请求指定尺寸的图片并保留元数据,但直接处理Data的方式更贴合你当前的流程

内容的提问来源于stack exchange,提问作者Jim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:24:54