ZIP Foundation 是一个用于创建、读取和修改 ZIP 压缩文件的库。它使用 Swift 编写,并基于 Apple 的 libcompression,以实现高性能和高能源效率。要了解有关该框架的性能特征的更多信息,您可以阅读这篇博客文章。
Swift Package Manager 是一个与 Swift 构建系统集成的依赖项管理器。要了解如何为您的项目使用 Swift Package Manager,请阅读官方文档。
要将 ZIP Foundation 添加为依赖项,您必须将其添加到您的 Package.swift
文件的 dependencies
中,并在您的 target
中引用该依赖项。
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "<Your Product Name>",
dependencies: [
.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMajor(from: "0.9.0"))
],
targets: [
.target(
name: "<Your Target Name>",
dependencies: ["ZIPFoundation"]),
]
)
添加依赖项后,您可以使用以下命令获取库:
$ swift package resolve
Carthage 是一个去中心化的依赖项管理器。
安装说明可以在项目的 README 文件中找到。
要使用 Carthage 将 ZIPFoundation 集成到您的 Xcode 项目中,您必须将其添加到您的 Cartfile
github "weichsel/ZIPFoundation" ~> 0.9
将 ZIPFoundation 添加到 Cartfile
后,您必须通过运行以下命令获取源代码:
carthage update --no-build
获取的项目必须通过将 ZIPFoundation.xcodeproj
拖到 Xcode 的项目导航器中来集成到您的工作区。(请参阅 官方 Carhage 文档。)
CocoaPods 是一个用于 Objective-C 和 Swift 的依赖项管理器。
要了解有关为 CocoaPods 设置项目的更多信息,请参阅官方文档。
要使用 CocoaPods 将 ZIP Foundation 集成到您的 Xcode 项目中,您必须将其添加到您的项目的 Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'ZIPFoundation', '~> 0.9'
end
之后,运行以下命令
$ pod install
ZIP Foundation 提供了两种高级方法来压缩和解压缩项目。两者都作为 FileManager
的扩展实现。
这些方法的功能仿照 macOS 中“归档实用工具”的行为。
要压缩单个文件,您只需传递一个文件 URL,表示您要压缩的项目,以及一个目标 URL 给 FileManager.zipItem(at sourceURL: URL, to destinationURL: URL)
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: currentWorkingPath)
sourceURL.appendPathComponent("file.txt")
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("archive.zip")
do {
try fileManager.zipItem(at: sourceURL, to: destinationURL)
} catch {
print("Creation of ZIP archive failed with error:\(error)")
}
默认情况下,创建的压缩文件不进行任何压缩。要创建压缩的 ZIP 压缩文件,必须将可选的 compressionMethod
参数设置为 .deflate
。
相同的方法也接受表示目录项目的 URL。在这种情况下,zipItem
会将 sourceURL
的目录内容添加到压缩文件中。
默认情况下,会向目标压缩文件添加一个以 sourceURL
的 lastPathComponent
命名的根目录条目。如果您不想在压缩文件中保留源文件的父目录,则可以传递 shouldKeepParent: false
。
要解压缩现有的压缩文件,您可以使用 FileManager.unzipItem(at sourceURL: URL, to destinationURL: URL)
。
这将递归地将压缩文件中的所有条目提取到目标 URL
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var sourceURL = URL(fileURLWithPath: currentWorkingPath)
sourceURL.appendPathComponent("archive.zip")
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("directory")
do {
try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
try fileManager.unzipItem(at: sourceURL, to: destinationURL)
} catch {
print("Extraction of ZIP archive failed with error:\(error)")
}
ZIP Foundation 还允许您单独访问特定条目,而无需提取整个压缩文件。此外,它还具有增量更新压缩文件内容的能力。
要访问特定的 ZIP 条目,您必须使用表示现有压缩文件的文件 URL 初始化一个 Archive
对象。完成此操作后,可以通过它们的相对路径检索条目。Archive
符合 Sequence
协议,因此支持下标访问
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
archiveURL.appendPathComponent("archive.zip")
let archive = try Archive(url: archiveURL, accessMode: .read)
guard let entry = archive["file.txt"] else {
return
}
var destinationURL = URL(fileURLWithPath: currentWorkingPath)
destinationURL.appendPathComponent("out.txt")
do {
try archive.extract(entry, to: destinationURL)
} catch {
print("Extracting entry from archive failed with error:\(error)")
}
extract
方法接受可选参数,允许您控制压缩和内存消耗。
您可以在该方法的文档中找到有关这些参数的详细信息。
要创建新的 Archive
,请传入一个不存在的文件 URL 和 AccessMode.create
。
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
archiveURL.appendPathComponent("newArchive.zip")
let archive = try Archive(url: archiveURL, accessMode: .create)
您可以向使用 .create
或 .update
AccessMode
打开的压缩文件添加或删除条目。要从现有文件添加条目,您可以将相对路径和基本 URL 传递给 addEntry
。相对路径标识 ZIP 压缩文件中的条目。相对路径和基本 URL 必须构成指向您要添加到压缩文件的文件的绝对文件 URL
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
archiveURL.appendPathComponent("archive.zip")
let archive = try Archive(url: archiveURL, accessMode: .update)
var fileURL = URL(fileURLWithPath: currentWorkingPath)
fileURL.appendPathComponent("file.txt")
do {
try archive.addEntry(with: fileURL.lastPathComponent, relativeTo: fileURL.deletingLastPathComponent())
} catch {
print("Adding entry to ZIP archive failed with error:\(error)")
}
或者,可以使用 addEntry(with path: String, fileURL: URL)
方法来添加不共享公共基本目录的文件。fileURL
参数必须包含一个绝对文件 URL,该 URL 指向任意文件系统位置上的文件、符号链接或目录。
addEntry
方法接受多个可选参数,允许您控制压缩、内存消耗和文件属性。
您可以在该方法的文档中找到有关这些参数的详细信息。
要删除条目,您需要引用压缩文件中的条目,您可以将其传递给 removeEntry
guard let entry = archive["file.txt"] else {
return
}
do {
try archive.remove(entry)
} catch {
print("Removing entry from ZIP archive failed with error:\(error)")
}
ZIP Foundation 还允许您使用 ZIP 条目内容,而无需将其写入文件系统。extract
方法接受类型为 Consumer
的闭包。在提取条目的内容耗尽之前,会调用此闭包
try archive.extract(entry, consumer: { (data) in
print(data.count)
})
传递到闭包的 data
包含当前条目的数据块。您可以通过提供可选的 bufferSize
参数来控制条目的数据块大小。
您还可以从内存数据源添加条目。为此,您必须将类型为 Provider
的闭包提供给 addEntry
方法
let string = "abcdefghijkl"
guard let data = string.data(using: .utf8) else { return }
try? archive.addEntry(with: "fromMemory.txt", type: .file, uncompressedSize: Int64(data.count), bufferSize: 4, provider: { (position, size) -> Data in
// This will be called until `data` is exhausted (3x in this case).
return data.subdata(in: Data.Index(position)..<Int(position)+size)
})
调用闭包,直到提供足够的数据以创建 uncompressedSize
的条目。闭包接收 position
和 size
参数,以便您可以管理数据源的状态。
除了基于闭包的基于文件的压缩文件读取和写入之外,ZIP Foundation 还提供了处理内存压缩文件的功能。这允许创建或提取仅驻留在 RAM 中的压缩文件。此功能的一个用例是动态创建稍后发送给客户端的 ZIP 压缩文件 - 而无需执行任何磁盘 IO。
要使用内存压缩文件,必须使用 init(data: Data, accessMode: AccessMode)
初始化器。
要读取或更新内存压缩文件,传入的 data
必须包含有效 ZIP 压缩文件的表示形式。
要创建内存压缩文件,可以省略 data
参数
let string = "Some string!"
let archive = try Archive(accessMode: .create)
guard let data = string.data(using: .utf8) else { return }
try archive.addEntry(with: "inMemory.txt", type: .file, uncompressedSize: Int64(data.count), bufferSize: 4, provider: { (position, size) -> Data in
return data.subdata(in: Data.Index(position)..<Int(position)+size)
})
let archiveData = archive.data
所有 Archive
操作都接受可选的 progress
参数。通过传入 Progress 的实例,您表明您要跟踪当前 ZIP 操作的进度。ZIP Foundation 会自动配置 progress
对象的 totalUnitCount
,并持续更新其 completedUnitCount
。
要获取有关当前操作完成工作的通知,您可以将键值观察器附加到 progress
对象的 fractionCompleted
属性。
ZIP Foundation FileManager
扩展方法也接受可选的 progress
参数。zipItem
和 unzipItem
都自动创建进度对象层次结构,以反映目录或包含多个项目的压缩文件中包含的所有项目的进度。
cancel() 方法的 Progress
可用于终止未完成的 ZIP 操作。如果取消,则当前操作会抛出 ArchiveError.cancelledOperation
异常。
ZIP Foundation 由 Thomas Zoechling 编写和维护。
Twitter: @weichsel。
ZIP Foundation 在 MIT 许可证下发布。
有关详细信息,请参阅 LICENSE。