iOS 如何实现 ZIP / RAR / 7z 解压?从压缩格式、流式解压到密码压缩架构

57 阅读18分钟

iOS 如何实现 ZIP / RAR / 7z 解压?从压缩格式、流式解压到密码压缩架构

在前面的文章中,我们已经把文件浏览器的基础架构逐渐搭起来:

01 iOS 文件浏览器架构
02 GB 级大文件处理
03 Wi-Fi 文件传输
04 SMB / NAS
05 WebDAV
06 FTP

从这一篇开始,我们回到文件浏览器最常见、也是最容易被普通用户感知到的一类功能:

压缩与解压

很多用户安装文件管理器,第一次真正用到它,可能就是因为收到一个:

archive.zip

或者:

project.rar

甚至:

backup.7z

iPhone 上打不开。

于是用户真正的问题非常简单:

这个压缩包怎么打开?

但对于开发者来说,背后并不只是调用一个:

unzip()

真正需要处理的问题包括:

  • ZIP、RAR、7z 有什么区别
  • TAR、GZIP 为什么又不一样
  • 密码压缩如何处理
  • 大压缩包如何避免爆内存
  • 解压进度如何计算
  • 用户取消以后怎么办
  • 同名文件如何解决冲突
  • 如何防止 Zip Slip 路径穿越
  • 如何防止压缩炸弹
  • 压缩包里面几万个文件怎么办
  • 如何支持预览而不全部解压
  • 如何把解压任务纳入 FileOperationManager

所以这一篇我们从架构角度拆解:

一个真正可靠的 iOS 压缩解压模块应该如何设计?


1. “压缩包”其实不是一种格式

从用户角度:

.zip
.rar
.7z

都可以统称:

压缩包。

但是技术上,它们是完全不同的格式。

例如:

ZIP
RAR
7z
TAR
GZIP
XZ
BZIP2

它们在:

  • 文件结构
  • 压缩算法
  • 加密方式
  • 索引方式
  • 随机访问能力
  • 多文件支持

等方面都有明显区别。

因此:

ArchiveService 不应该建立在“所有压缩包都一样”的假设上。


2. ZIP 是什么?

ZIP 是最常见的归档格式之一。

它可以同时:

Archive
+
Compress

也就是说,它既可以:

把多个文件装进一个容器

又可以:

对其中的数据进行压缩。

例如:

project.zip
│
├── README.md
├── Images/
│   ├── 001.jpg
│   └── 002.jpg
└── Documents/
    └── report.pdf

ZIP 很大的优势是:

Compatibility

Windows、macOS、iOS 以及各种文件工具都普遍支持。

所以如果用户问:

应该把文件压缩成什么格式方便分享?

很多时候 ZIP 都是很安全的选择。


3. RAR 又是什么?

RAR 同样是一种归档格式。

用户在 Windows 环境中尤其容易遇到:

movie.rar
software.rar
photos.rar

RAR 在压缩率、分卷等方面拥有自己的格式能力。

但对于 App 开发来说,一个重要现实是:

RAR 并不像 ZIP 那样可以简单地认为“系统天然支持”。

开发者通常需要使用相应的解析/解压实现,并且需要认真确认所使用实现的:

License
Distribution Terms
Format Support

尤其如果要把功能集成到商业 App 中,不能只看:

GitHub 上能不能跑。

还要确认:

这个库是否允许以你的商业方式进行分发和使用。


4. 7z 为什么经常压得更小?

7z 是另一种常见归档格式。

它经常搭配:

LZMA
LZMA2

等压缩算法使用。

很多场景中,7z 可以获得比较高的压缩率。

于是用户经常会看到:

1.2GB Folder
      ↓
7z
      ↓
700MB Archive

当然:

压缩率不是免费的。

通常需要在:

Compression Ratio
      ↕
CPU Time
      ↕
Memory

之间进行权衡。

压得越狠,并不意味着在移动设备上体验一定越好。


5. TAR 和 ZIP 其实不是同一类东西

很多开发者第一次看到:

.tar.gz

会把它理解成一种“压缩格式”。

其实更准确地说:

TAR

主要负责:

Archive

也就是:

把很多文件打包成一个连续的数据流。

例如:

Folder/
├── A
├── B
└── C

先:

TAR
 ↓
archive.tar

再:

GZIP
 ↓
archive.tar.gz

所以:

.tar.gz

可以理解为:

Archive
+
Compression

两个步骤组合。

这和 ZIP:

Archive + Compression

集成在同一个格式里的思路并不完全一样。


6. 因此需要统一的 Archive Format 模型

不要在业务代码里到处写:

if ext == "zip" { ... }
else if ext == "rar" { ... }
else if ext == "7z" { ... }

更合理的是先建立统一模型:

enum ArchiveFormat {
    case zip
    case rar
    case sevenZip
    case tar
    case gzip
    case bzip2
    case xz
}

然后:

File
 ↓
ArchiveDetector
 ↓
ArchiveFormat
 ↓
ArchiveService

格式判断也不要完全依赖:

File Extension

更可靠的实现可以结合:

Extension
+
Magic Number
+
Header

判断。

因为一个文件叫:

photo.zip

并不代表里面一定真的是 ZIP。


7. ArchiveService 应该和 FileProvider 分开

前几篇我们一直在构建:

FileProvider

它负责:

Local
SMB
WebDAV
FTP

这些“文件来自哪里”的问题。

ArchiveService 解决的是:

拿到文件以后怎么处理。

所以:

FileProvider
      ↓
FileItem
      ↓
ArchiveService

而不是:

LocalZipProvider
SMBZipProvider
FTPZipProvider

否则组合会迅速爆炸。

架构应该保持:

Source
   ↓
FileProvider
   ↓
FileItem
   ↓
ArchiveService

8. 可以定义统一的 ArchiveService

例如:

protocol ArchiveService {

    func listEntries(
        in archive: FileItem
    ) async throws -> [ArchiveEntry]

    func extract(
        archive: FileItem,
        to destination: FileLocation
    ) async throws

    func createArchive(
        from items: [FileItem],
        format: ArchiveFormat,
        to destination: FileLocation
    ) async throws
}

核心抽象:

ArchiveService
      │
      ├── List
      ├── Extract
      └── Compress

至于底层:

ZIP
RAR
7z

应该由具体实现决定。


9. 为什么“查看压缩包内容”和“解压”要分开?

很多用户只是想:

看看压缩包里面有什么。

如果一个:

15GB archive.zip

用户点一下就自动全部解压,

体验显然很差。

所以应该支持:

Archive
  ↓
Read Metadata / Index
  ↓
Show Entries

例如:

project.zip
│
├── README.md
├── Photos/
├── Documents/
└── source/

用户可以先浏览。

需要某个文件时再:

Extract Selected

这意味着 ArchiveService 至少需要:

List Entries

和:

Extract

两套能力。


10. ArchiveEntry 应该独立建模

例如:

struct ArchiveEntry {
    let path: String
    let name: String
    let isDirectory: Bool
    let compressedSize: Int64?
    let uncompressedSize: Int64?
    let modifiedDate: Date?
}

UI 看到的是:

ArchiveEntry

而不是具体:

ZIP Central Directory Record
RAR Header
7z Entry

这样不同格式就可以共享:

Archive Browser UI

11. 解压大文件也必须考虑内存

假设压缩包里有:

video.mp4
20GB

错误思路:

Compressed Entry
      ↓
Decompress
      ↓
20GB Data
      ↓
Memory

基本不可接受。

正确方向:

Compressed Stream
       ↓
Decompressor
       ↓
Chunk
       ↓
Destination File

核心仍然是:

Streaming。

也就是说:

Archive Size = 30GB

不应该意味着:

Memory = 30GB

12. 解压实际上是一条数据管线

可以把一个文件的解压理解为:

Archive File
     ↓
Compressed Bytes
     ↓
Decoder
     ↓
Uncompressed Bytes
     ↓
Chunk
     ↓
Output File

所以可以进一步设计:

ArchiveReader
      ↓
Decompressor
      ↓
OutputWriter

这和前面的大文件传输架构非常相似。

区别只是中间多了:

Decompression

13. 解压进度应该怎么算?

表面上很简单:

processed / total

但这里有两个不同的 total:

Compressed Size

和:

Uncompressed Size

例如:

archive.7z
Compressed:
1GB

Uncompressed:
5GB

进度可以基于:

Processed Entries
Processed Compressed Bytes
Processed Uncompressed Bytes

不同库能够提供的信息不同。

对于用户而言,更重要的是:

Extracting...

2.6 GB / 5 GB
52%

所以 Archive Operation 最好把底层进度统一映射成:

0.0 ... 1.0

而不是让 UI 理解每一种格式的内部细节。


14. 一个压缩包可能有几十万个文件

比如:

node_modules.zip

里面可能有海量小文件。

这种压缩包和:

一个 20GB 视频

是完全不同的压力模型。

前者压力来自:

File Count
Directory Creation
Metadata
Small IO

后者压力来自:

Large Sequential IO

因此 ArchiveService 不能只优化:

Large File

还需要考虑:

Huge Entry Count

15. 不要一次把 100,000 个 Entry 全塞进 UI

例如:

100,000 Archive Entries

如果全部:

Parse
 ↓
Create Model
 ↓
Render

可能会造成明显性能压力。

所以可以考虑:

Lazy Parsing
Pagination
Batch Updates
Virtualized List

和 NAS 大目录的思路一样:

内容很多,不代表首屏必须全部准备好。


16. 解压必须支持取消

用户开始:

Extract 50GB Archive

到:

61%

发现选错目录。

如果不能取消,只能干等。

所以 Archive Operation 需要:

Cancel

例如每处理一定数量数据:

try Task.checkCancellation()

逻辑:

Entry
 ↓
Chunk
 ↓
Check Cancel
 ↓
Chunk
 ↓
Check Cancel

最终:

Cancel
 ↓
Stop Decoder
 ↓
Close Files
 ↓
Cleanup

17. 取消以后已经解压的文件怎么办?

这是产品设计必须明确的问题。

假设:

archive.zip

1000 Files

已经解压:

600 Files

用户取消。

应该:

方案 A

全部删除:

Rollback

方案 B

保留已经成功的文件。

方案 C

让用户选择。

对于普通文件管理器,我更倾向于:

默认尽量保证目标目录处于可理解状态。

一种实现方式是:

Extract
 ↓
Temporary Directory
 ↓
Complete
 ↓
Move to Final Destination

失败:

Temporary Directory
 ↓
Delete

成功:

Temporary Directory
 ↓
Finalize

这样事务边界更清晰。


18. 但是临时目录会占用双倍空间吗?

这是一个非常现实的问题。

如果压缩包解压后:

40GB

临时目录再移动到最终目录,

如果底层移动可以通过同一卷上的 rename/move 完成,

通常不需要重新复制全部内容。

但如果:

Temporary

和:

Destination

位于不同 Provider 或不同文件系统,

可能就需要真实的数据迁移。

所以:

Transactional Extract

也需要结合:

Destination Provider

设计。


19. 同名文件冲突怎么办?

假设目标目录已经有:

report.pdf

压缩包里也有:

report.pdf

不能偷偷覆盖。

应该明确策略:

Replace
Keep Both
Skip
Cancel

如果是整个压缩包:

Apply to All

也很重要。

例如:

[✓] Apply to all conflicts

否则一个 1000 文件压缩包可能连续弹几十次。


20. Keep Both 要生成安全的新名字

例如:

report.pdf

已经存在。

生成:

report (1).pdf
report (2).pdf

但注意:

Directory

也可能冲突。

所以冲突策略应该属于:

FileOperation / Destination Resolver

而不是 ZIP 解压器内部随便处理。

这样:

Copy
Move
Extract
Download

都可以共享冲突逻辑。


21. 密码压缩怎么处理?

例如用户打开:

private.zip

ArchiveService 发现:

Encrypted

UI 显示:

Enter Password

用户输入:

********

然后:

Password
   ↓
Archive Decoder
   ↓
Decrypt
   ↓
Decompress

密码不要:

写入普通日志

也不要:

长期放在普通 UserDefaults

如果用户选择:

记住这个压缩包密码

可以考虑安全存储。


22. ZIP 加密也有不同类型

看到:

Password Protected ZIP

不能假设所有密码 ZIP 都一样。

历史上 ZIP 存在较旧的传统加密方式。

现代工具也可能使用:

AES

等更强的加密方案。

所以 App 声称支持:

Encrypted ZIP

时,需要明确实际底层实现支持哪些加密方式。

否则很容易出现:

某些密码 ZIP 能开
某些不能开

用户会以为是 Bug。


23. 7z 密码还有一个特殊点:文件名也可能被隐藏

有些加密归档不仅:

File Data

被加密。

连:

File Names

也可能受到保护。

这意味着用户甚至无法先浏览:

Archive Entries

而需要:

Password
 ↓
Read Header
 ↓
Show Entries

所以 Archive Browser 需要允许:

Locked

这种状态。


24. 密码错误应该是独立错误类型

不能:

Extract Failed

就结束。

应该区分:

Wrong Password
Unsupported Encryption
Corrupted Archive
Not Enough Space
Cancelled
Permission Denied

例如:

enum ArchiveError: Error {
    case wrongPassword
    case unsupportedFormat
    case unsupportedEncryption
    case corruptedArchive
    case insufficientStorage
    case invalidEntryPath
    case cancelled
    case unknown(Error)
}

这样 UI 才能告诉用户:

密码错误,请重新输入。

而不是:

解压失败。


25. Zip Slip:压缩解压里非常重要的安全问题

假设一个恶意 ZIP 内部路径:

../../../../Library/secret.txt

如果解压逻辑只是:

destination
    .appendingPathComponent(entry.path)

然后直接写文件,

攻击者就可能尝试把文件写出目标目录。

这类问题通常被称为:

Zip Slip / Path Traversal。


26. 每一个 Entry Path 都必须验证

例如目标:

/Documents/Extracted/

Entry:

../../evil.txt

拼接后:

/Documents/Extracted/../../evil.txt

如果直接规范化,

可能逃离:

Extracted/

所以必须:

Entry Path
    ↓
Normalize
    ↓
Resolve
    ↓
Check Destination Root
    ↓
Allowed?

只有:

Final Path ∈ Destination Root

才允许写入。

核心原则:

压缩包里的路径永远不能被当成可信输入。


27. 绝对路径同样不能信

例如 Entry:

/private/var/...

或者某些奇怪的 Windows 风格路径:

C:\...

都应该在 Archive 层做标准化和拒绝。

不能让归档文件自己决定:

我要写到设备上的哪里。

归档只应该影响:

Destination Root

下面的相对路径。


28. 还要警惕符号链接

假设压缩包中创建:

link -> ../../somewhere

后续 Entry 再写入:

link/file

理论上可能绕过普通字符串路径检查。

因此如果底层格式支持:

Symlink

解压器还需要明确策略:

Reject
Preserve Safely
Resolve Carefully

尤其文件管理 App 不应该默认盲目信任归档里的链接。


29. 什么是压缩炸弹?

压缩包还有另一类风险:

Decompression Bomb。

例如一个非常小的压缩包:

42KB

解压后可能膨胀成:

Several GB

甚至更大。

形式上:

Tiny Archive
    ↓
Huge Expansion
    ↓
Disk Full
    ↓
Resource Exhaustion

这类归档可能是恶意的,也可能只是极端压缩数据。


30. 不能只看压缩包本身大小

错误判断:

Archive = 10MB

所以:

一定没问题。

实际上真正应该尽量估计:

Total Uncompressed Size

例如:

Compressed:
100MB

Uncompressed:
80GB

就需要谨慎。

可以在解压前计算:

Estimated Output Size
      ↓
Available Storage
      ↓
Enough?

明显不够就提前失败。


31. 还要限制解压文件数量

压缩炸弹不一定只靠尺寸。

也可能是:

1,000,000 Tiny Files

即使总大小不是特别大,

文件系统操作也可能造成巨大压力。

所以安全策略可以考虑:

Maximum Entry Count
Maximum Expanded Size
Maximum Compression Ratio
Maximum Nested Depth

具体限制要结合产品场景设计。


32. 嵌套压缩包尤其危险

例如:

a.zip
  ↓
b.zip
  ↓
c.zip
  ↓
d.zip

如果 App 自动递归:

Auto Extract Everything

风险会快速放大。

所以普通文件管理器最好不要:

发现压缩包就无限自动继续解。

更安全的是:

Extract Current Archive

完成后由用户决定是否继续打开内部归档。


33. 压缩文件时也需要流式处理

创建:

backup.zip

同样不能:

100 Files
 ↓
All Data in Memory
 ↓
Compress

更合理:

File A
 ↓
Stream
 ↓
Compressor
 ↓
Archive

File B
 ↓
Stream
 ↓
Compressor
 ↓
Archive

所以:

Compress

和:

Extract

都应该围绕:

Streaming Pipeline

设计。


34. 压缩等级应该让用户选择吗?

例如:

Fast
Balanced
Maximum

对应:

Speed
 ↕
Compression Ratio

对于普通用户,直接展示:

Level 0-9

未必友好。

可以转换成:

Fast
Standard
Best Compression

高级设置里再提供更细控制。

这也是:

技术参数不一定应该原样暴露给用户。


35. 压缩视频往往不会小很多

用户可能选择:

movie.mp4

然后 ZIP。

发现:

2.00GB
 ↓
1.98GB

因为 MP4 内部本身已经使用视频编码压缩。

类似还有:

JPEG
HEIC
MP3
AAC

它们本身已经高度压缩。

所以文件压缩和:

Video Compression
Image Compression
Audio Compression

不是一回事。

ZIP:

对文件数据进行归档/通用压缩。

视频压缩:

重新编码媒体内容。

这一点很适合在产品里给用户解释清楚。


36. ArchiveService 不应该负责视频转码

如果用户:

movie.mp4

想从 2GB 变成 300MB,

应该进入:

MediaService

而不是:

ArchiveService

所以:

ArchiveService
      ↓
ZIP / 7z / TAR

MediaService
      ↓
Video / Audio / Image Compression

职责必须分开。


37. 压缩任务应该进入统一 FileOperationManager

例如用户:

Select 500 Files
      ↓
Compress
      ↓
archive.zip

这本质仍然是:

Long Running Operation

应该拥有:

Waiting
Running
Progress
Cancel
Completed
Failed

所以可以:

OperationType.compress
OperationType.extract

纳入:

FileOperationManager

任务中心:

Creating archive.zip

2.1 GB / 5.0 GB

42%

38. Archive Operation 可以复用现有基础设施

前几篇已经拥有:

Progress
Cancellation
Conflict Handling
Temporary File
Error Mapping
Storage Check

压缩解压无需重新发明。

例如:

Extract
 ↓
FileOperationManager
 ↓
ArchiveService
 ↓
Temporary Destination
 ↓
Progress
 ↓
Finalize

这样架构才能长期维护。


39. 远程压缩包怎么办?

这是统一架构真正有意思的地方。

比如:

NAS
 ↓
backup.zip

用户想打开。

最简单的方法:

SMB
 ↓
Download Entire ZIP
 ↓
Local
 ↓
ArchiveService

但如果:

backup.zip = 50GB

显然体验不好。

更高级的实现可以考虑:

Remote Range Read

让 ArchiveService 通过:

FileProvider.read(offset:length:)

按需读取。


40. 为什么 ZIP 很适合随机访问?

ZIP 的目录信息通常让客户端能够先获得:

Entry Index

再读取特定 Entry 所需的数据区域。

如果 FileProvider 支持:

Random Access

理论上就可以:

Remote ZIP
    ↓
Read Archive Index
    ↓
Show Entries
    ↓
User selects one file
    ↓
Read Required Range

而不是:

Download 50GB ZIP

之后才能看到里面有什么。

这对 NAS / WebDAV 文件浏览器非常有价值。


41. 所以 ArchiveService 最终也需要抽象数据源

一开始可能是:

ArchiveService(URL)

更进一步可以变成:

ArchiveDataSource

例如:

protocol ArchiveDataSource {
    var size: Int64 { get }

    func read(
        offset: Int64,
        length: Int
    ) async throws -> Data
}

于是:

Local File
     ↓
LocalArchiveDataSource

SMB File
     ↓
RemoteArchiveDataSource

WebDAV
     ↓
RemoteArchiveDataSource

ArchiveService 不需要知道:

文件到底在哪。

它只需要:

给我指定范围的数据。


42. 这就是前面 FileProvider 抽象的延伸

整个链路可以变成:

SMB / WebDAV / Local
        ↓
    FileProvider
        ↓
 Random Access
        ↓
 ArchiveDataSource
        ↓
  ArchiveService
        ↓
 ArchiveEntry

于是:

Remote Archive Preview

就成为可能。

这也是为什么文件管理器底层抽象设计得好以后,新功能会越来越容易复用。


43. 创建密码 ZIP 时需要把“安全”当作功能的一部分

如果用户选择:

Create Encrypted Archive

UI 不应该只提供:

Password

最好还要处理:

Confirm Password

以及:

  • 是否显示密码
  • 密码强度提示
  • 忘记密码无法恢复的说明

因为压缩包密码和 App 登录密码完全不同。

如果归档格式本身不提供恢复机制:

用户忘记密码,App 通常也无法帮他恢复文件。

这一点必须提前说明。


44. Archive Password 可以进入专门的密码管理层

例如:

Archive Password
       ↓
Temporary Use

默认只在当前任务中存在。

如果用户明确选择:

Remember Password

再进入安全存储。

例如:

Archive Password Store
        ↓
Keychain

这样和:

SMB Credential
WebDAV Credential
FTP Credential

在安全基础设施上可以复用。


45. 一个完整的 Archive 架构

最终可以整理成:

┌─────────────────────────────┐
│             UI              │
│                             │
│ Archive Browser             │
│ Compress Sheet              │
│ Extract Progress            │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│     FileOperationManager    │
│                             │
│ Compress                    │
│ Extract                     │
│ Progress                    │
│ Cancel                      │
│ Conflict                    │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│        ArchiveService       │
│                             │
│ Detect                      │
│ List Entries                │
│ Extract                     │
│ Create                      │
└──────────────┬──────────────┘
               │
        ┌──────┼───────┐
        ▼      ▼       ▼
       ZIP    RAR      7z
        │
        └──────┬───────┘
               ▼
       ArchiveDataSource
               │
     ┌─────────┼─────────┐
     ▼         ▼         ▼
   Local      SMB      WebDAV

旁边还需要:

Password Store
Security Validator
Path Validator
Storage Checker
Conflict Resolver

46. TS File Explorer 为什么需要统一 ArchiveService?

从开发者角度:

ZIP
RAR
7z
TAR
GZIP

都是完全不同的格式。

但用户根本不想了解这些。

用户只想:

收到文件以后能打开。

例如:

微信 / 邮件 / 网盘
      ↓
project.rar
      ↓
TS File Explorer
      ↓
查看内容
      ↓
Extract
      ↓
使用文件

用户真正需要的不是:

一个 RAR Decoder。

而是:

一个能够处理压缩文件的统一入口。

这也是文件浏览器产品非常重要的一层价值。


47. 压缩功能也应该遵循“协议差异隐藏在底层”

就像:

SMB
WebDAV
FTP

最终统一成:

FileProvider

一样。

ZIP
RAR
7z
TAR

最终也应该统一成:

ArchiveService

于是用户看到的永远只有:

Open
Preview
Extract
Compress
Password

而不是:

这个格式走 A API
那个格式走 B API

48. 开发压缩解压功能前,建议先回答这 20 个问题

如果准备实现完整的 Archive 模块,建议先回答:

1. 支持哪些格式?

2. 格式如何检测?

3. 是否支持只查看内容?

4. 是否支持选择性解压?

5. 大文件是否流式处理?

6. 解压进度如何计算?

7. 压缩进度如何计算?

8. 用户取消以后如何清理?

9. 同名文件怎么处理?

10. 是否支持密码 ZIP?

11. 支持哪些加密方式?

12. 密码如何保存?

13. 如何防止 Zip Slip?

14. 如何处理 Symlink?

15. 如何防止压缩炸弹?

16. 如何处理几十万个 Entry?

17. 是否支持远程 Archive?

18. 是否支持 Random Access?

19. Archive Operation 如何进入任务中心?

20. 第三方库的 License 是否适合商业分发?

如果这些问题没有提前考虑,压缩解压很容易从:

一个简单功能

变成:

性能问题
+
安全问题
+
兼容问题
+
大量格式特殊逻辑

写在最后

ZIP / RAR / 7z 看起来只是:

Compress
Extract

两个按钮。

真正实现以后,会发现它背后实际上包含:

Format Detection
      +
Streaming IO
      +
Encryption
      +
Progress
      +
Cancellation
      +
Conflict Handling
      +
Path Security
      +
Resource Protection

所以对于文件浏览器来说:

Archive 模块不应该只是几个第三方解压 API 的集合,而应该是一套独立的文件处理系统。

到目前为止,这个系列已经从:

File Browser

逐渐发展成:

File Platform

整个路线:

01 File Browser Architecture
        ↓
02 Large File IO
        ↓
03 Wi-Fi Transfer
        ↓
04 SMB / NAS
        ↓
05 WebDAV
        ↓
06 FTP
        ↓
07 ZIP / RAR / 7z

下一篇我建议进入另一个和 TS File Explorer 很匹配、同时非常适合普通用户搜索的主题:

《iOS 图片格式转换怎么实现?HEIC、JPEG、PNG、WebP 与图片压缩架构》

下一篇可以系统讲:

HEIC → JPG
PNG → JPG
WebP → PNG
图片压缩
分辨率调整
批量处理
EXIF
内存峰值

这样内容会从“文件协议”进一步扩展到真正的:

文件处理能力。