如何创建 HTML/ZIP/PNG 多用途文件
引言
SingleFile 等网页归档工具常使用数据 URI 存储网页资源,但对于大资源效率低下。通过结合 ZIP 格式的灵活结构与 HTML,并进一步封装到 PNG 文件中,可以创建更优雅的解决方案。
ZIP 格式的强大功能
ZIP 格式提供组织化的结构来存储多个文件,基于文件条目和中央目录。中央目录充当目录表,包含每个文件条目的元数据(如文件名、大小、校验和和偏移量)。ZIP 的灵活性体现在:通过设置第一个文件条目的偏移量大于 0,可以在 ZIP 内容前添加数据;通过 ZIP 注释(最多 64KB)可以在后附加数据。这使其非常适合创建多用途文件。
创建 HTML/ZIP 多用途文件
基于 ZIP 的灵活性,可以创建一个在浏览器中工作的自解压归档文件。网页及其资源存储在 ZIP 文件中,ZIP 数据嵌入 HTML 注释中。自解压页面的结构如下:
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Please wait...</title>
<script> Content of lib/zip.min.js </script>
</head>
<body>
<p>Please wait...</p>
<!-- [ZIP data] -->
<script> Content of assets/main.js </script>
</body>
</html>
启动页面使用 fetch("") 读取 ZIP 数据,并通过 JavaScript 库提取内容以替换自身。但当从文件系统打开时(除 Firefox 外),由于同源策略限制,直接使用 fetch("") 会失败。
从 DOM 读取 ZIP 数据以克服限制
为解决文件系统限制,可以直接从 DOM 读取 ZIP 数据。此方法需要处理字符编码问题:
- DOM 文本内容解码为 UTF-16 而非 windows-1252。
- NULL 字符(U+0000)被解码为替换字符(U+FFFD)。
- 回车符(\r)和回车换行符(\r\n)被解码为换行符(\n)。
前两个问题可通过关联表转换为 windows-1252 解决。对于第三个问题,在启动页面中添加 JSON 脚本标签作为“巩固数据”,跟踪回车符和回车换行符的偏移量,以便准确重建原始内容。启动页面编码改为 windows-1252,结构如下:
<!doctype html>
<html>
<head>
<meta charset=windows-1252>
<title>Please wait...</title>
<script> Content of lib/zip.min.js </script>
</head>
<body>
<p>Please wait...</p>
<!-- [ZIP data] -->
<script type=application/json>
[consolidation data]
</script>
<script> Content of assets/main.js </script>
</body>
</html>
引入 PNG 格式
PNG 格式由签名和块组成,每个块包含长度、类型标识符(如 IHDR、IDAT、IEND、tEXt)、数据内容和 CRC32 校验和。最小 PNG 结构包括:
- PNG 签名(8 字节)
- IHDR 块(13 字节)
- 一个或多个 IDAT 块
- IEND 块(12 字节)
最终形式:HTML/ZIP/PNG 多用途文件
将 HTML、ZIP 和 PNG 结合到一个文件中,利用 HTML 的容错能力。最终结构作为 PNG 块表示如下:
[PNG 签名]
[IHDR 块]
[tEXt 块: 包含 HTML 开头部分]
[IDAT 块(s): 包含 HTML 中间部分(如 ZIP 数据注释)]
[tEXt 块: 包含 HTML 剩余部分]
[IEND 块]
此结构引入新挑战:
- PNG 签名和块(如 IHDR、IEND)会短暂显示为文本节点,需在页面解析后立即移除。
- 显示页面在怪异模式下渲染,需使用
document.write() 等方法解析显示页面。
通过图像重用优化
优化措施包括:移除主图像(如 RennesJS 徽标)并重用页面(作为 PNG 文件)替换显示页面中的图像,从而减少文件大小。
结果文件
提供演示文件 demo.png.zip.html 供下载和查看。注意:macOS 上的“归档实用工具”存在解压缩 bug,可使用 unzip 绕过。