ZIPFoundation 0.9.19

ZIPFoundation 0.9.19

测试已测试
语言语言 SwiftSwift
许可协议 MIT
发布最后发布2024 年 4 月
SPM支持 SPM

维护者:Thomas Zoechling



Swift Package Manager compatible Carthage compatible CocoaPods Compatible Platform Twitter

ZIP Foundation 是一个用于创建、读取和修改 ZIP 归档文件的库。
它使用 Swift 编写并基于 Apple 的 libcompression 以实现高性能和能效。
要了解更多关于框架的性能特性,您可以阅读这篇博客文章

特性

  • 现代 Swift API
  • 高性能压缩和解压缩
  • 大文件支持
  • 内存归档
  • 确定性内存消耗
  • 与 Linux 兼容
  • 无第三方依赖(在 Apple 平台上,Linux 上的 zlib)
  • 全面的单元和性能测试覆盖率
  • 完整的文档

要求

  • iOS 12.0+ / macOS 10.11+ / tvOS 12.0+ / watchOS 2.0+
  • 或带有 zlib 开发包的 Linux
  • Xcode 11.0
  • Swift 4.0

安装

Swift包管理器

Swift包管理器是集成在Swift构建系统中的依赖管理器。若要了解如何将Swift包管理器用于您的项目,请参阅官方文档
要将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

Carthage是一种去中心化的依赖性管理器。
可以在这项项目的README文件中找到安装说明。

要使用Carthage将ZIPFoundation集成到您的Xcode项目中,您必须将其添加到您的Cartfile中。

github "weichsel/ZIPFoundation" ~> 0.9

将ZIPFoundation添加到Cartfile后,您必须通过运行以下命令获取源代码:

carthage update --no-build

获取的项目必须通过将ZIPFoundation.xcodeproj拖放到Xcode的项目导航器中来集成到您的工作空间中。(参见官方Carthage文档。)

CocoaPods

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。在这种情况下,zipItemsourceURL的目录内容添加到存档中。
默认情况下, destination archive会在名为sourceURLlastPathComponent的根目录条目添加到目标存档中。如果您不想在存档中保留源父目录,则可以传递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基础库还允许您无需提取整个存档即可访问特定条目。此外,它还提供了按需更新存档内容的能力。

访问单个条目

要获取对特定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方法接受一些可选参数,这些参数允许您控制压缩率和内存消耗。
您可以在该方法的文档中找到有关这些参数的详细信息。

创建存档

要创建一个新的 存档,传入一个不存在的文件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

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

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 的条目之前被调用。闭包接收 positionsize 参数,这样您就可以管理数据源的状态。

内存归档

除了基于关闭的文件归档的读取和写入之外,ZIP基金会还提供了处理内存归档的能力。这允许创建或提取仅存在于RAM中的归档。此功能的用例之一是动态创建稍后发送给客户端的ZIP文件归档,而无需执行任何磁盘I/O。

要处理内存归档,必须使用 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基金会自动配置progress对象的totalUnitCount,并不断更新其completedUnitCount
要获取有关当前操作完成工作的通知,可以为您的progress对象的fractionCompleted属性附加一个Key-Value Observer。
ZIP基金会的FileManager扩展方法也接受可选的progress参数。《code>zipItem和《code>unzipItem都自动创建一个代表了目录中或包含多个项目的归档的所有项目的进度对象层次结构。

Progresscancel()方法可用于终止未完成的ZIP操作。在取消的情况下,当前操作会抛出ArchiveError.cancelledOperation异常。

致谢

ZIP基金会由Thomas Zoechling编写和维护。
推特:@weichsel

许可证

ZIP基金会采用MIT许可证发布。
有关详细信息,请参阅LICENSE