这是 weichsel 的 ZIPFoundation 的一个分支,有两个目标:
zlib
C 源代码作为包依赖项提供,从而消除对系统安装的 zlib
的需求,增加对非 Apple 操作系统的可移植性。FileHandle
替换它们,以及(对于内存中的归档文件)为 Data
编写文件句柄包装器,从而增加对非 POSIX/Linux 系统(目前仅测试 Windows)的兼容性。我尽可能保留了原始测试,并在测试套件中添加了 Windows 环境。 假设可能存在损坏的东西,但就我的用例而言,它似乎至少在 macOS、Linux 和 Windows 上与原始 ZIPFoundation 一样稳定。
以下是原始仓库中修改后的自述文件。
ZIP Foundation 是一个用于创建、读取和修改 ZIP 归档文件的库。
它用 Swift 编写,并且基于 Apple 的 libcompression,以实现高性能和高能效。
要了解有关该框架的性能特征的更多信息,您可以阅读这篇博客文章。
Swift Package Manager 是一个与 Swift 构建系统集成的依赖项管理器。要了解如何为您的项目使用 Swift Package Manager,请阅读官方文档。
要将 ZIP Foundation 添加为依赖项,您必须将其添加到 Package.swift
文件的 dependencies
中,并在您的 target
中引用该依赖项。
// swift-tools-version:5.7
import PackageDescription
let package = Package(
name: "<Your Product Name>",
dependencies: [
.package(url: "https://github.com/gregcotten/ZIPFoundationModern", .upToNextMajor(from: "0.0.1")),
],
targets: [
.target(
name: "<Your Target Name>",
dependencies: [
.product(name: "ZIPFoundation", package: "ZIPFoundationModern")
]),
]
)
添加依赖项后,您可以使用以下命令获取库:
$ swift package resolve
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")
guard let archive = Archive(url: archiveURL, accessMode: .read) else {
return
}
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 currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
archiveURL.appendPathComponent("newArchive.zip")
guard let archive = Archive(url: archiveURL, accessMode: .create) else {
return
}
您可以将条目添加到已使用 .create
或 .update
AccessMode
打开的归档文件,或从中删除条目。 要从现有文件中添加条目,您可以将相对路径和基本 URL 传递给 addEntry
。 相对路径标识 ZIP 归档文件中的条目。 相对路径和基本 URL 必须形成一个绝对文件 URL,该 URL 指向您要添加到归档文件中的文件
let fileManager = FileManager()
let currentWorkingPath = fileManager.currentDirectoryPath
var archiveURL = URL(fileURLWithPath: currentWorkingPath)
archiveURL.appendPathComponent("archive.zip")
guard let archive = Archive(url: archiveURL, accessMode: .update) else {
return
}
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
参数来控制条目的块大小。
您还可以从内存中的数据源添加条目。 为此,您必须向 addEntry
方法提供 Provider
类型的闭包
let string = "abcdefghijkl"
guard let data = string.data(using: .utf8) else { return }
try? archive.addEntry(with: "fromMemory.txt", type: .file, uncompressedSize: UInt64(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: position..<position+size)
})
调用闭包,直到提供了足够的数据以创建 uncompressedSize
的条目。 闭包接收 position
和 size
参数,以便您可以管理数据源的状态。
除了基于闭包的文件归档文件的读取和写入之外,ZIP Foundation 还提供处理内存中归档文件的功能。 这允许创建或提取仅驻留在 RAM 中的归档文件。 此功能的一个用例是动态创建稍后发送到客户端的 ZIP 归档文件 - 无需执行任何磁盘 IO。
要使用内存中的归档文件,必须使用 init(data: Data, accessMode: AccessMode)
初始化器。
要读取或更新内存中的归档文件,传入的 data
必须包含有效 ZIP 归档文件的表示形式。
要创建内存中的归档文件,可以省略 data
参数
let string = "Some string!"
guard let archive = Archive(accessMode: .create),
let data = string.data(using: .utf8) else { return }
try? archive.addEntry(with: "inMemory.txt", type: .file, uncompressedSize: UInt64(data.count), bufferSize: 4, provider: { (position, size) -> Data in
return data.subdata(in: position..<position+size)
})
let archiveData = archive.data
所有 Archive
操作都接受可选的 progress
参数。 通过传入 Progress 的实例,您可以表明要跟踪当前 ZIP 操作的进度。 ZIP Foundation 会自动配置 progress
对象的 totalUnitCount
,并持续更新其 completedUnitCount
。
要获取有关当前操作已完成工作的通知,您可以将键值观察器附加到 progress
对象的 fractionCompleted
属性。
ZIP Foundation FileManager
扩展方法也接受可选的 progress
参数。 zipItem
和 unzipItem
都自动创建进度对象层次结构,这些对象反映目录中包含的所有项目或包含多个项目的归档文件的进度。
可以使用 Progress
的 cancel() 方法终止未完成的 ZIP 操作。 如果取消,当前操作将抛出 ArchiveError.cancelledOperation
异常。
此分支 ZIPFoundationModern
由 Greg Cotten 适配和维护。
原始的 ZIPFoundation 包由 Thomas Zoechling 编写和维护。
ZIP Foundation 在 MIT 许可证下发布。
有关详细信息,请参阅 LICENSE。