ZIP 也能边下载边解压?流式解压技术揭秘!

简介:对于一个 ZIP 文件,由于标准的解压方式总是从读取文件的末尾开始的,因此必须下载完整个 ZIP 解压后才能访问。当用户通过网络访问 ZIP 文件时,下载解压所带来的耗时将大大降低用户体验。那么能不能边下载边解压呢?阿里巴巴文娱技术 喻远将介绍 ZIP 流式解压的原理和技术实现路径。


打开网络上的 ZIP 文件需要几步?下载,解压,拿到所有文件。面对一个 ZIP,能不能「边下边播」、「按需下载」?

今年 6 月,优酷绘本技术团队开发出新的解压方式——ZIP 流式解压技术,并成功应用在优酷绘本秒开项目中,30M+ 绘本平均加载时长只需 0.91s,加载耗时比传统的解压方式降低了 88.3%,让用户的阅读体验直线提升。实际对比效果如下:

ZIP 也能边下载边解压?流式解压技术揭秘!_第1张图片

ZIP 也能边下载边解压?流式解压技术揭秘!_第2张图片

本文将介绍 ZIP 流式解压的原理和技术实现路径,希望为大家带来启发,将 ZIP 流式解压技术更多的应用到业务中。

一 什么是ZIP

ZIP 是一种文件格式,定义了如何将多个文件、数据块组织在一起形成一个完整的文件。例如我们常见的 .apk,.ipa,.sketch,都是ZIP文件。通常程序是这样创建 ZIP 文件的:

  • 压缩单个文件形成单文件数据块;
  • 在数据块前后添加文件描述信息;
  • 对每个待压缩的文件重复以上步骤后,拼接所有数据形成更大的数据块;

提取所有文件描述信息,生成一份「文件目录」,附在最后一个数据块的尾部。

我们将文件前部描述信息称为 Local File Header,文件后部描述信息称为 Data Descriptor, 被压缩的文件本身称为 File Data,将最后的文件目录称为 Central Directory。以上所有合在一起,就是一个标准的 ZIP 文件。如下图:

ZIP 也能边下载边解压?流式解压技术揭秘!_第3张图片

一个标准的解压方式总是从读取 ZIP 文件末尾开始的,我们以解压上图的 File Data 1 为例:

ZIP 也能边下载边解压?流式解压技术揭秘!_第4张图片

  • 首先在 ZIP 文件末尾找到 Central Directory 数据块;
  • 在 Central Directory 数据块中找到 File Header 1;
  • 从 File Header 1 中读取 Local File Header 1 的偏移量和 File Data 1 的相关信息;
  • 根据偏移量找到 Local File Header 1;
  • 读取 Local File Header 1;
  • 解密 File Data 1(如果需要);
  • 解压 File Data 1;
  • 读取 Data Descriptor 1;
  • 使用 File Header 1 中保存的 CRC-32 做校验步骤 7 中计算的 CRC-32,以确保解压后的数据完整性。

标准解压方式存在的不足

可以发现,标准的解压强依赖尾部的 Central Directory。当 ZIP 文件存储在 cdn 上时,哪怕我们只想访问其中的一个文件,也必须下载整个 ZIP 解压后才可访问。假如 ZIP 文件有 100 MB,但是我们只需要访问其中的某一个 10 KB 的文件,那么下载整个 ZIP 将是对流量的巨大浪费。

二 优酷技术方案:ZIP流式解压

我们的一个初步的想法是能不能边下载边解压?

要实现这点,首先需要改变解压方式,使其不能再依赖尾部的 Central Directory。

根据 ZIP 文件格式标准可知,除了 Central Directory,每个 File Data 头部的 Loca File Header 部分也包含了该文件的相关信息。

假如 Local File Header 中包含了充分的信息,我们也许可以基于 Local File Header 去解压文件数据,其解压流程就可以变为:

ZIP 也能边下载边解压?流式解压技术揭秘!_第5张图片

  • 从头开始,搜索到 Local File Header 1;
  • 读取 Local File Header 1;
  • 解密 File Data 1(如果需要);
  • 解压 File Data 1;
  • 读取 Data Descriptor 1;
  • CRC32 的校验。
  • 那么 Local File Header 里到底存储了什么?是否满足解密解压所需?

了解 Local File Header

我们根据文档对 Local File Header 的描述,画出其二进制文件中的排列:

ZIP 也能边下载边解压?流式解压技术揭秘!_第6张图片

其中的关键信息为:

ZIP 也能边下载边解压?流式解压技术揭秘!_第7张图片

元数据签名是一个 Magic Number,用来标记接下来数据是什么内容。例如 Local File Header 的签名是 0x04034b50,用 char 表示也就是 { 'P', 'K', '3', '4' }。当读取到对应数据签名时,则意味着接下来的数据结构符合对应元数据的定义,需要使用对应规则解析。

Compress Method 指明数据块用何种算法压缩,解压需要使用对应的算法。

Compressed Size 和 UnCompressed Size可以帮助确定文件的结尾地址和 Data Descriptor 的偏移量。这两个 Size 也是文件解密时 HMAC 计算的关键。

有了 Magic Number 作为元数据签名,我们只需要逐字节遍历去匹配这个 Number,就可以找到 Loca File Header,而不再需要依赖尾部的定位信息。而且 Local File Header 中存储的元数据足够我们决定解压算法、计算大小、校验 CRC-32 了。

还有一个问题是,解压缩算法是否支持流式解压缩?是否有特定的上下文依赖?通过了解压缩算法的原理[1],我们知道,所有的压缩算法都是支持从头部开始流式解压的。

而下载方面,文件是以从头到尾连续的方式下载,这又天然地和和从头解压的方式配合,便可以初步实现边下边解!

加密 ZIP 文件的问题

一切都相当顺利,直到遇到了加密后的 ZIP 文件。加密后的 ZIP 文件的 Local File Header 中的关键信息除了签名和文件名以外,其他信息都被隐去,需要去 Central Directory 中读取。

再一次,我们回到了依赖 Central Directory 的状态。

在失去如此多关键信息的情况下能否继续做到流式解压?我们需要先挖掘一下 ZIP 的加密方式。

ZIP 的加密方式

ZIP 文件支持多种加密方式,最常见的是 Traditional PKWARE Encryption 和 AES Encryption 。

Traditional PKWARE Encryption 是 ZIP 自定义的一种基于密码的对称加密方式,每个字节的加密仅和密码有关,加密前后的数据长度不变。这种不依赖上下文的加密方式可以实现我们需要的流式解密。

AES 加密采用的是 CTR 模式。CTR 模式将明文分组,并生成一个计数器。使用密钥对计数器进行加密生成二进制字节流。利用这个字节流和明文进行 XOR 操作进行加密。其解密方式也是一样的。

这种方式也支持流式解密。

ZIP 也能边下载边解压?流式解压技术揭秘!_第8张图片

两种常用的加密方式都支持流式解密,那么加解密需要的关键信息,在 Local File Header 中是否有存储就成了能否流式解密的关键。

流式解密的关键信息

无论是 Traditional PKWARE Encryption 还是 AES Encryption,在解密时都需要一些除密码之外的关键信息,例如盐值,加密算法的强度等。此外,在 AES 加密的 ZIP 文件中, Local File Header 中的 Compress Method 字段被抹去,这样我们便无法知晓压缩算法,因此无法解压。

至此,问题集中为:

  • Local File Header 中是否有足够的加密所需信息。
  • 加密的 ZIP 文件,是否能在除 Central Directory 以外的位置找到 Compress Method 字段。

Local File Header 中加密相关的信息

ZIP 格式的设计者在设计 ZIP 文件格式的初期就提供了文件拓展能力,一些额外的拓展数据可以存放在 Local File Header 的 Extra Field 中。ZIP AES 加密说明书[2]告诉我们 AES 的相关信息就存放在这里。其关键信息如下:

ZIP 也能边下载边解压?流式解压技术揭秘!_第9张图片

原来压缩算法被藏到了 Extra Data 中。那么盐值被存放在哪里了?答案是存放在 File Data 的头尾。

ZIP 也能边下载边解压?流式解压技术揭秘!_第10张图片

综上,我们找到解密所需的所有关键信息,整个流式解密解压的所有技术点都被我们探索完。剩下的便是按原理实现,以及细节的打磨。

三 总结

说了那么多,流式解压究竟有什么价值呢?

由于流式解压实现了边下载边解压,将整个操作的时长从下载 + 解压缩变成了约等于纯下载的时长,直接抹掉了解压的耗时。在 39.1 MB 大小的 ZIP 包下载解压测试中,耗时从 9.08 秒降低至 4.17 秒,有将近 100% 的提速!同时,你可以不必等待整个 ZIP 下载解压完,而是在解压完一小部分数据的时候,就直接展示 UI。用户侧看起来就好像一瞬间就解压完了。

因此,流式解压可以应用在许多时间敏感的操作里,也可以用来优化基于 ZIP 文件的相关业务。例如基于 ZIP 的全局换肤加速、基于 ZIP 的 Web 资源缓存加载的加速等等。前言中的优酷绘本秒开就是基于这一技术实现。

参考

[1]https://houbb.github.io/2018/11/09/althgorim-compress-althgorim-12-zip-02
[2]AES Encryption Information: Encryption Specification AE-1 and AE-2
https://www.winzip.com/win/en/aes_info.html
[3]ZIP File Format Specification
https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.2.1.TXT
[4]AES Coding Tips for Developers
https://www.winzip.com/win/en/aes_tips.html

原文链接:https://developer.aliyun.com/article/771846?

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

你可能感兴趣的:(存储,缓存,算法,搜索推荐,文件存储,数据安全/隐私保护,CDN)