跳到主要内容

手写两个编码器:ICO 与 ZIP

· 3 分钟阅读
Connexion Collective
ADMIN 权限狗

新加的两个工具都要「凭空造文件」:一个把 PNG 打包成 .ico,一个把几段文本打成 .zip。

按惯例会装两个库。但这两个格式的复杂度被高估了 —— 值得先看看能不能自己写。

ICO:六个字节的文件头加一张目录​

格式简单得有点意外:

ICONDIR 6 字节 保留位(2) + 类型(2, 1=图标) + 数量(2)
ICONDIRENTRY 16 字节×N 宽 高 调色板 保留 色平面 位深 数据长度 数据偏移
数据区 每个尺寸一张图

Vista 之后允许在 ICO 里直接塞 PNG,所以「数据区」就是若干个 PNG 拼在一起,不需要写 BMP 编码。

两个容易翻车的细节:

  • 256 尺寸要写成 0。宽高字段各只有一个字节,装不下 256,规范里用 0 代替。
  • 偏移量从文件开头算,不是从数据区算。第一张图的数据偏移等于 6 + 16 × 尺寸数。

验证也简单:生成后读回字节,前四个字节应该是 00 00 01 00,然后顺着目录里每个条目记录的偏移去找,那里应该是 PNG 签名 89 50 4E 47。四个尺寸四个签名全对上,说明目录没算错。

ZIP:只需要两段结构​

ZIP 的完整实现很复杂(压缩算法、数据描述符、ZIP64),但只用 store 模式的话,本质是三块:

本地文件头 + 文件数据 (每个文件一份)
中央目录 (每个文件一条记录)
EOCD (目录的位置和条目数)

没有任何压缩,所以不需要接 deflate;只要算对 CRC32、偏移量和长度即可。

几个必须注意的点:

  • CRC32 要算原始字节,不是编码后的字符串长度。先生成 Uint8Array 再算。
  • 文件名长度按字节算,中文名在 UTF-8 下是多字节,还得在标志位里设 0x0800 告诉解压器「这是 UTF-8」。
  • 中央目录里的偏移指向对应的本地文件头,不是数据本身。
  • EOCD 里要写中央目录的起始偏移和总长度,写错一个字节整个包就废。

生成的包我用系统自带的解压验证过:pack.mcmeta、函数文件、标签 JSON 都能正常解出来,说明 CRC 和偏移都对。

代价与边界​

手写的代价是不支持压缩。数据包这种几 KB 的文本,store 模式完全够用;但如果要打包几十 MB 的素材,还是应该用带 deflate 的库。

另一个代价是没有容错:不校验输入、不支持 ZIP64(超过 4 GB 或 65535 个文件)。对「生成一个下载包」这个用途来说,这些都用不上。

换来的是:零依赖、零供应链风险、产物可控。这两个编码器加起来不到 200 行,读一遍就知道它在干什么 —— 这比在 node_modules 里多两个包踏实。

加载评论中...