You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS/Swift无法原生解压多文件Zip包,求原生支持的替代格式

iOS/Swift 原生多文件打包解压方案(无第三方库)

1. 优先推荐:.tar.gz 格式(基于 libarchive + Compression 框架)

iOS 原生通过 Foundation 间接支持 libarchive,可以直接处理 .tar 多文件归档,配合系统 Compression 框架实现压缩,完全满足多文件打包+原生解压需求,无需第三方库。

打包流程

  • 第一步:将多个本地文件打包为 .tar 归档
    使用 Archive 类(导入 Foundation 即可),指定归档路径与写入模式,通过 addEntry 方法逐个添加文件,同时保留文件名、目录结构等元数据。
  • 第二步:对 .tar 文件做 gzip 压缩
    利用 Compression 框架的 compression_encode_buffer 函数,将 .tar 的二进制数据压缩为 gzip 格式,最终生成 .tar.gz 包。

解压流程

  • 第一步:解码 gzip 数据
    用 compression_decode_buffer 函数将 .tar.gz 的二进制数据还原为原始 .tar 文件数据。
  • 第二步:解析 .tar 归档并提取文件
    再次使用 Archive 类读取 .tar 数据,遍历每个归档条目,将内容写入对应路径的本地文件,自动还原原目录结构。

2. 自定义容器 + Accelerate 流压缩(复杂度较高)

如果一定要用 Accelerate 框架的流压缩能力,需要自己定义简单的容器格式来存储多文件元数据:

  • 打包时:
    1. 先写入自定义头部,包含文件总数、每个文件的名称、大小、偏移量;
    2. 用 Accelerate 的 vImage 压缩API逐个处理文件内容,将压缩后的数据依次写入输出文件。
  • 解压时:
    1. 先读取头部解析出所有文件的元信息;
    2. 按偏移量读取对应压缩数据,用 Accelerate 解压后写入本地文件。
      这种方式需要手动处理容器解析,复杂度远高于 .tar.gz 方案,仅适合特殊场景。

3. 不推荐:NSKeyedArchiver 归档(仅适用于原生对象)

如果打包的是Swift/OC原生对象(如自定义模型、图片数据),可以用 NSKeyedArchiver 将多个对象归档为单个文件,但这种方式仅支持可归档对象,对视频、文档等通用文件兼容性差,不适合作为通用多文件打包方案。

核心代码示例(.tar.gz 解压)

import Foundation
import Compression

func extractTarGzip(from data: Data, to directoryURL: URL) throws {
    // 第一步:解压gzip数据
    let decompressedTarData = try data.decompressed(using: .gzip)
    
    // 第二步:解析tar归档并提取文件
    let archive = try Archive(data: decompressedTarData, accessMode: .read)
    for entry in archive {
        let targetURL = directoryURL.appendingPathComponent(entry.path)
        // 创建父目录
        try FileManager.default.createDirectory(at: targetURL.deletingLastPathComponent(), withIntermediateDirectories: true)
        
        if entry.type == .directory {
            try FileManager.default.createDirectory(at: targetURL, withIntermediateDirectories: false)
        } else {
            try entry.readData().write(to: targetURL)
        }
    }
}

// Data扩展:实现gzip解压
extension Data {
    func decompressed(using algorithm: CompressionAlgorithm) throws -> Data {
        let bufferSize = 4096
        var decompressedData = Data()
        
        let sourceBuffer = self.withUnsafeBytes { $0.bindMemory(to: UInt8.self) }
        var sourceIndex = sourceBuffer.startIndex
        
        let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        defer { destinationBuffer.deallocate() }
        
        var stream = compression_stream()
        var status = compression_stream_init(&stream, COMPRESSION_STREAM_DECODE, algorithm.rawValue)
        guard status != COMPRESSION_STATUS_ERROR else {
            throw NSError(domain: "CompressionError", code: -1, userInfo: nil)
        }
        defer { compression_stream_destroy(&stream) }
        
        stream.src_ptr = sourceBuffer.baseAddress! + sourceIndex
        stream.src_size = self.count
        stream.dst_ptr = destinationBuffer
        stream.dst_size = bufferSize
        
        repeat {
            status = compression_stream_process(&stream, COMPRESSION_STREAM_FINALIZE)
            switch status {
            case COMPRESSION_STATUS_OK, COMPRESSION_STATUS_END:
                let writtenBytes = bufferSize - stream.dst_size
                decompressedData.append(destinationBuffer, count: writtenBytes)
                stream.dst_ptr = destinationBuffer
                stream.dst_size = bufferSize
            case COMPRESSION_STATUS_ERROR:
                throw NSError(domain: "CompressionError", code: -1, userInfo: nil)
            default:
                break
            }
        } while status == COMPRESSION_STATUS_OK
        
        return decompressedData
    }
}

enum CompressionAlgorithm: Int32 {
    case gzip = COMPRESSION_GZIP
    case lzfse = COMPRESSION_LZFSE
    case zlib = COMPRESSION_ZLIB
}

内容的提问来源于stack exchange,提问作者Fattie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:08:28