如何在不将图片读入内存的情况下为已有图片URL添加元数据?
解决方案
你可以通过两种方式实现无需加载整张图片到内存即可添加元数据的需求,以下是具体方案:
方案一:直接修改本地图片文件的元数据(推荐)
利用ImageIO框架的API,无需加载整张图片像素数据,仅操作元数据部分修改本地文件,之后再通过fileURL导入Photos。这种方式兼容性最好,元数据会直接嵌入图片文件本身。
示例代码:
import ImageIO import Photos import Foundation func addMetadataToImageFile(at url: URL, metadata: [String: Any]) throws { // 创建图片源(仅读取元数据,不加载像素) guard let source = CGImageSourceCreateWithURL(url as CFURL, nil) else { throw NSError(domain: "ImageMetadataError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无法创建图片源"]) } let imageType = CGImageSourceGetType(source)! // 创建临时文件路径 let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()) .appendingPathComponent("\(UUID().uuidString).\(url.pathExtension)") // 创建图片目标,用于写入修改后的内容 guard let destination = CGImageDestinationCreateWithURL(tempURL as CFURL, imageType, 1, nil) else { throw NSError(domain: "ImageMetadataError", code: 2, userInfo: [NSLocalizedDescriptionKey: "无法创建图片写入目标"]) } // 合并原有元数据与新元数据 let existingMetadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [String: Any] ?? [:] var combinedMetadata = existingMetadata combinedMetadata.merge(metadata) { _, new in new } // 从源写入图片与合并后的元数据(不加载整张图到内存) CGImageDestinationAddImageFromSource(destination, source, 0, combinedMetadata as CFDictionary) // 完成写入 guard CGImageDestinationFinalize(destination) else { throw NSError(domain: "ImageMetadataError", code: 3, userInfo: [NSLocalizedDescriptionKey: "无法写入修改后的图片"]) } // 替换原文件 try FileManager.default.removeItem(at: url) try FileManager.default.moveItem(at: tempURL, to: url) } // 使用示例 do { // 假设你的图片文件URL和要添加的元数据 let imageFileURL = URL(fileURLWithPath: "/path/to/your/large-image.jpg") let exifMetadata = [ kCGImagePropertyExifDictionary as String: [ kCGImagePropertyExifDateTimeOriginal as String: "2024:05:20 14:30:00" ] ] // 先修改本地文件元数据 try addMetadataToImageFile(at: imageFileURL, metadata: exifMetadata) // 再导入到Photos let creationRequest = PHAssetCreationRequest.forAsset() let options = PHAssetResourceCreationOptions() options.originalFilename = "custom-filename.jpg" creationRequest.addResource(with: .photo, fileURL: imageFileURL, options: options) PHPhotoLibrary.shared().performChanges({ // 提交创建请求 }) { success, error in if let error = error { print("导入Photos失败: \(error.localizedDescription)") } else { print("导入成功") } } } catch { print("修改元数据失败: \(error.localizedDescription)") }
方案二:单独添加元数据资源(兼容性有限)
通过PHAssetCreationRequest添加类型为.metadata的资源,这种方式无需修改原图片文件,但兼容性较差——不是所有图片格式都支持分离的元数据资源,且Photos不一定会自动将元数据合并到图片本身,后续读取时可能需要单独获取元数据资源。
示例代码:
import Photos import Foundation do { let imageFileURL = URL(fileURLWithPath: "/path/to/your/large-image.jpg") let exifMetadata = [ kCGImagePropertyExifDictionary as String: [ kCGImagePropertyExifDateTimeOriginal as String: "2024:05:20 14:30:00" ] ] // 将元数据转为Data(元数据体积很小,不会占用过多内存) let metadataData = try NSKeyedArchiver.archivedData(withRootObject: exifMetadata, requiringSecureCoding: false) let creationRequest = PHAssetCreationRequest.forAsset() let options = PHAssetResourceCreationOptions() options.originalFilename = "custom-filename.jpg" // 添加图片资源 creationRequest.addResource(with: .photo, fileURL: imageFileURL, options: options) // 添加元数据资源 creationRequest.addResource(with: .metadata, data: metadataData, options: nil) PHPhotoLibrary.shared().performChanges({ // 提交创建请求 }) { success, error in // 处理结果 } } catch { print("操作失败: \(error.localizedDescription)") }
内容的提问来源于stack exchange,提问作者Jeshua Lacock
相关产品推荐
相关产品推荐

