iOS 将压缩的 NSData 写入文件会生成损坏的 zip 文件

iOS writing compressed NSData to a file generates corrupted zip file

我试图在我的 iOS 应用程序中实现一个非常简单的事情:将文件压缩到 Documents 文件夹中。我写了以下 FileManager 扩展名:

extension FileManager {


    func zipFile(url: URL, deleteOriginal: Bool = false) {
        guard let fileData = try? Data(contentsOf: url) else { return }
        let nsdata = fileData as NSData
        let zipped: NSData
        zipped = (try? nsdata.compressed(using: .zlib)) ?? nsdata
        let zip = zipped as Data
        let zipUrl = url.deletingPathExtension().appendingPathExtension("zip")
        try? FileManager.default.removeItem(at: zipUrl)
        try? zip.write(to: zipUrl)
        if deleteOriginal {
            try? FileManager.default.removeItem(at: url)
        }
    }

}

但是,创建的文件似乎已损坏。

我去Xcode -> Window -> 设备和模拟器、select 我的设备,然后是它下方列表中的我的应用程序,单击列表下方的齿轮按钮,然后单击 下载容器... 以查看文件。然后我打开下载包的内容并检查 Documents 文件夹——里面有创建的 zip 文件。但是,我无法打开它。默认情况下 Mac 创建一个扩展名为 zip.cpgz 的文件,当文件损坏时通常会这样做。其他提取器应用程序会显示一条错误消息,告知文件已损坏。当我尝试使用 unzip 在终端中打开它时,我看到以下消息:

iMac:Downloads user$ unzip myzip.zip
Archive:  myzip.zip
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of myzip.zip or
        myzip.zip.zip, and cannot find myzip.zip.ZIP, period.

我不仅尝试了 NSData.compressed,还尝试了一个名为 DataCompression, and also implemented compression "myself" using Apple instructions from here 的库。在所有情况下,文件都以相同的方式损坏。

我还尝试调试我的代码,在调试器中显示压缩的数据对象并将其导出为文件。再次损坏并出现相同的症状。

我做错了什么吗?请让我知道如何将内容压缩并保存到 文件,而不仅仅是 [NS]Data,就像 Whosebug 中的大多数答案一样。

ZIP 是一种存档文件格式,也支持压缩(但它也可以包含未压缩的文件)。

ZIP 存档不仅仅包含压缩文件内容。它还维护所有文件、目录和符号链接的索引。该索引称为中央目录,它附加到文件的末尾。此外,存档中的每个文件还会附加一些元数据。此本地文件头包含文件创建日期、文件属性、文件路径等信息,...
您可以找到 ZIP 格式的完整规范 here

我在 Swift 中发布了一个可用于处理 ZIP 存档的小框架:https://github.com/weichsel/ZIPFoundation

使用此框架,您可以使用以下代码从单个文件创建存档:

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)")
}