在 Swift (iOS/macOS) 中处理 ZIP/归档
在现代 iOS 和 macOS 开发中,经常需要将文件压缩成 ZIP 归档或从中提取数据。这可能是为了节省空间、通过网络传输数据或组织备份。在本文中,我们将探讨两种主要方法:使用 Foundation 的内置功能和流行的第三方库 ZipFoundation。
Foundation 内置工具 (NSFileCoordinator + 底层操作)
Apple 在 Foundation 中没有提供用于处理 ZIP 的高级 API。然而,从 iOS 12 / macOS 10.14 开始,可以使用 NSFileCoordinator 和 NSFileWrapper 进行某些操作。但对于完整的 ZIP 处理,通常通过 C 接口使用系统工具 libz。
示例:使用 libz 压缩数据
import Compressionimport Foundation
func compressData(_ data: Data) -> Data? { let sourceData = data as NSData let compressedData = NSMutableData(length: data.count)! var compressedSize = data.count let result = compression_encode( compressedData.mutableBytes.assumingMemoryBound(to: UInt8.self), &compressedSize, sourceData.bytes.assumingMemoryBound(to: UInt8.self), data.count, nil, COMPRESSION_ZLIB ) guard result == COMPRESSION_STATUS_OK else { return nil } compressedData.length = compressedSize return compressedData as Data}ZipFoundation 库(推荐方式)
ZipFoundation 是一个轻量级的开源库,用于在 Swift 中处理 ZIP 归档。它支持 iOS、macOS、tvOS 和 watchOS。通过 Swift Package Manager 安装:
// 在 Package.swift 或通过 Xcodedependencies: [ .package(url: "https://github.com/weichsel/ZIPFoundation.git", from: "0.9.0")]ZipFoundation 的主要功能:
- 从文件和文件夹创建 ZIP 归档
- 提取单个文件或整个归档
- 支持密码 (AES-256)
- 支持大文件的流式处理
- 与 macOS 和 iOS 兼容
示例 1:创建 ZIP 归档
import ZIPFoundationimport Foundation
func createZip(from sourceURL: URL, to destinationURL: URL) throws { let fileManager = FileManager() // 从文件夹创建归档 try fileManager.zipItem(at: sourceURL, to: destinationURL) // 或者从单个文件创建 // try fileManager.zipItem(at: sourceURL, to: destinationURL, // shouldKeepParent: false)}
// 使用let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!let sourceFolder = documents.appendingPathComponent("MyData")let archiveURL = documents.appendingPathComponent("backup.zip")
do { try createZip(from: sourceFolder, to: archiveURL) print("归档已创建: \\(archiveURL.path)")} catch { print("错误: \\(error)")}示例 2:解压 ZIP 归档
import ZIPFoundation
func unzipArchive(at sourceURL: URL, to destinationURL: URL) throws { let fileManager = FileManager() // 如果目标文件夹不存在则创建 try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true) // 解压 try fileManager.unzipItem(at: sourceURL, to: destinationURL)}
// 使用let archiveURL = documents.appendingPathComponent("backup.zip")let extractFolder = documents.appendingPathComponent("ExtractedData")
do { try unzipArchive(at: archiveURL, to: extractFolder) print("解压完成")} catch {