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

核心提示阿里妹导读:对于一个 ZIP 文件,由于标准的解压方式总是从读取文件的末尾开始的,因此必须下载完整个 ZIP 解压后才能访问。当用户通过网络访问 ZIP 文件时,下载解压所带来的耗时将大大降低用户体验。那么能不能边下载边解压呢?阿里巴巴文娱

阿里的指南:对于一个ZIP文件,由于标准的解压方式总是从读取文件结束开始,所以必须下载整个ZIP文件并解压后才能访问。当用户通过网络访问ZIP文件时,下载和解压的耗时会大大降低用户体验。那么可以下载解压吗?阿里巴巴娱乐科技御园将介绍ZIP流媒体解压的原理和技术实现路径。

文富礼:该上低代码开发训练营了!

在网络上打开一个ZIP文件需要几个步骤?下载、解压并获取所有文件。面对一个ZIP,可以“边玩边点播”吗?今年6月,优酷绘本的技术团队开发了一种新的解压方式——ZIP流解压技术,并成功应用于优酷绘本的二次开放项目中。30M+绘本平均加载时间仅为0.91s,加载时间比传统解压方式减少了88.3%,使用户的阅读体验呈线性提升。实际对比效果如下:

优化前

后优化

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

ZIP是什么?

ZIP是一种文件格式,它定义了如何将多个文件和数据块组织在一起,形成一个完整的文件。比如我们常见的。apk,。国际音标和。草图是压缩文件。通常该程序会创建一个如下所示的ZIP文件:

压缩单个文件以形成单个文件数据块;

在数据块前后添加文件描述信息;

对每个待压缩文件重复上述步骤后,将所有数据拼接成一个较大的数据块;

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

我们称前面的文件描述信息为本地文件头,后面的文件描述信息为数据描述符,压缩文件本身为文件数据,最后的文件目录为中央目录。以上所有加在一起,就是一个标准的ZIP文件。如下图:

ZIP文件格式

标准的解压缩方法总是从读取ZIP文件的结尾开始。我们以上图中解压文件数据1为例:

首先,找到ZIP文件末尾的中心目录数据块;

在中央目录数据块中找到了fileheader1

从File Header 1中读取本地文件头1的偏移量和文件数据1的相关信息;

根据偏移量,找到localfileheader1

读取localfileheader1

解密filedata1

解压缩文件数据1;

读取数据描述符1;;

使用文件头1中存储的CRC-32来检查步骤7中计算的CRC-32,以确保解压缩数据的完整性。

标准解压缩方法的缺点

可以发现,标准解压缩强烈依赖于尾部中心目录。当ZIP文件存储在cdn上时,即使我们只想访问其中的一个,也必须下载整个ZIP并解压后才能访问。如果ZIP文件有100 MB,但我们只需要访问10 KB文件中的一个,那么下载整个ZIP文件将是巨大的流量浪费。

二、优酷技术解决方案:ZIP流媒体解压

我们最初的一个想法是能不能下载解压。

要实现这一点,我们首先需要改变解压方式,让它不再依赖于中央目录的尾部。

根据ZIP文件格式标准,除了中心目录之外,每个文件数据头的Loca文件头部分还包含文件的相关信息。

如果本地文件头包含足够的信息,我们也许能够基于本地文件头来解压缩文件数据,并且解压缩过程可以变成:

从头开始,搜索到本地文件头1;

读取localfileheader1

解密filedata1

解压缩文件数据1;

读取数据描述符1;;

CRC32的验证。

那么本地文件头中到底存储了什么呢?符合解密解压的要求吗?

了解本地文件头

根据文件中本地文件头的描述,我们画出其二进制文件的排列:

localheader数据结构

关键信息是:

元数据是一个幻数,用来标记下一个数据是什么。比如本地文件头的签名是0x04034b50,用char表示,即{'P ',' K ',' 3 ',' 4'}。当读取到对应的数据签名时,意味着下一个数据结构符合对应元数据的定义,需要通过对应的规则进行解析。

Compress Method表示使用哪种算法压缩数据块,解压缩需要使用相应的算法。

压缩大小和未压缩大小可以帮助确定文件的结束地址和数据描述符的偏移量。这两个大小也是解密文件时HMAC计算的关键。

以幻数作为元数据签名,我们只需要逐字节遍历匹配这个数,就可以找到Loca文件头,而不需要依赖尾部的位置信息。而存储在本地文件头的元数据足够我们决定解压算法,计算大小,检查CRC-32。

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

下载方面,文件从头到尾都是连续下载的,自然配合了一开始就解压的方式,可以初步实现边到边的解决方案!

加密ZIP文件的问题

一切都很顺利,直到我遇到了加密的ZIP文件。除了签名和文件名,加密ZIP文件的本地文件头中的密钥信息是隐藏的,因此需要在中心目录中读取。

我们又一次回到了依赖中央目录的状态。

即使丢失了这么多关键信息,还能继续流式解压吗?我们需要先探究一下ZIP的加密方法。

ZIP的加密方法

ZIP文件支持多种加密方法,其中最常见的是传统的PKWARE加密和AES加密。

传统的PKWARE加密是由ZIP定义的基于密码的对称加密方法。每个字节的加密只与密码有关,加密前后的数据长度不变。这种上下文无关的加密方法可以实现我们需要的流式解密。

AES加密采用CTR模式。CTR模式将明文分组并生成计数器。用密钥加密计数器以生成二进制字节流。这个字节流和明文用于加密的异或运算。解密方法是一样的。

此方法还支持流式解密。

两种常见的加密方式都支持流式解密,因此加密和解密所需的密钥信息是否存储在本地文件头中就成为流式解密的关键。

流解密的关键信息

无论是传统的PKWARE加密还是AES加密,密码以外的一些关键信息,比如盐值,加密算法的强度等。解密时需要。另外,在AES加密的ZIP文件中,本地文件头中的Compress Method字段被擦除,这样我们就无法知道压缩算法,所以无法解压缩。

此时,问题集中在:

本地报头中是否有足够的加密信息?

加密的ZIP文件,是否可以在中央目录以外的位置找到压缩方法字段。

本地标头中的加密相关信息

ZIP格式的设计者在设计ZIP文件格式的前期就提供了文件扩展能力,可以在本地文件头的额外字段中存储一些额外的扩展数据。ZIP加密规范[2]告诉我们,AES的相关信息都存储在这里。其关键信息如下:

原始压缩算法隐藏在额外的数据中。那么盐值储存在哪里呢?答案存储在文件数据的头尾。

综上所述,我们找到了解密需要的所有关键信息,整个流解密解压的所有技术点都是我们摸索出来的。剩下的就是根据原理去实现,去打磨细节。

三个总结

说了这么多,流式解压有什么价值?

因为流式解压实现了边下载边解压,把整个操作的时长从下载+解压变成了纯下载的时长左右,直接抹去了解压的耗时。在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加密信息:加密规范AE-1和AE-2 https://www . WinZip . com/win/en/AES _ info . html[3]ZIP文件格式规范https://pkware . cache fly . net/webdocs/app note/app note-6 . 2 . 1 . txt[4]AES编码

易大地代码开发训练营

实战课程

阿里面向低代码应用开发的PaaS平台“咿呀”即将开始面向各行各业开发者的首期训练营。四位技术大咖独家授课,手把手教你使用,帮你快速掌握页面、流程、报表的构建。从入门到精通只需要5天!

 
友情链接
鄂ICP备19019357号-22