在 Swift (iOS/macOS) 中处理 ZIP 归档文件
数据的压缩和解压是 iOS 和 macOS 应用开发中的关键任务之一。由于 ZIP 格式的通用性以及 Apple 操作系统内置的支持,它仍然是最流行的文件打包方式。在本文中,我们将探讨在 Swift 中处理 ZIP 归档文件的两种主要方法:使用内置的 Foundation 框架和强大的第三方库 Zip Foundation。
1. Foundation 内置工具 (NSFileCoordinator + libz)
从 iOS 9 和 macOS 10.11 开始,Apple 通过 NSFileCoordinator 类和系统库 libz 提供了对 ZIP 的有限但足以满足基本任务的支持。然而,这种方法需要手动管理线程,不适合复杂场景。
示例:使用 libz 解压 ZIP 归档文件
import Foundationimport Compression
func unzipFile(at sourceURL: URL, to destinationURL: URL) throws { let sourceData = try Data(contentsOf: sourceURL) // 使用系统解压缩器 let decodedData = try sourceData.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Data in let sourcePtr = ptr.baseAddress!.assumingMemoryBound(to: UInt8.self) let sourceSize = sourceData.count // 解压数据的缓冲区(通常大于原始数据) let bufferSize = sourceSize * 4 var buffer = Data(count: bufferSize) let decodedSize = buffer.withUnsafeMutableBytes { (destPtr: UnsafeMutableRawBufferPointer) -> Int in let dest = destPtr.baseAddress!.assumingMemoryBound(to: UInt8.self) return compression_decode_buffer(dest, bufferSize, sourcePtr, sourceSize, nil, COMPRESSION_ZLIB) } return buffer.prefix(decodedSize) } // 保存解压后的数据 try decodedData.write(to: destinationURL, options: .atomic)}内置方法的局限性:
- 不支持多文件归档
- 无法处理密码
- 需要手动管理缓冲区
- 处理大文件困难
2. Zip Foundation 库(推荐方法)
Zip Foundation 是一个开源库,为在 Swift 中处理 ZIP 归档文件提供了简单而优雅的 API。它支持:
- 创建和解压归档文件
- 处理密码 (AES-256)
- 通过 Combine 或闭包实现进度条
- 大文件的流式处理
- 支持 macOS、iOS、watchOS 和 tvOS
通过 Swift Package Manager 安装
// 在 Package.swift 文件或通过 Xcode:dependencies: [ .package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.0")]示例:创建 ZIP 归档文件
import Zip
func createZipArchive() { let fileManager = FileManager.default let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! // 收集要归档的文件 let file1 = documentsURL.appendingPathComponent("report.pdf") let file2 = documentsURL.appendingPathComponent("photo.jpg") let filesToZip = [file1, file2] // 未来归档文件的路径 let archiveURL = documentsURL.appendingPathComponent("backup.zip") do { // 通过闭包实现进度 try Zip.zipFiles(paths: filesToZip, zipFilePath: archiveURL, password: nil, progress: { (progress) in print("归档进度:\\(Int(progress * 100))%") }) print("归档文件已创建:\\(archiveURL.path)") } catch { print("创建归档文件时出错:\\(error.localizedDescription)") }}