ZIP 也能邊下載邊解壓?流式解壓技術揭秘!
開啟網路上的 ZIP 檔案需要幾步?下載,解壓,拿到所有檔案。面對一個 ZIP,能不能「邊下邊播」、「按需下載」?
今年 6 月,優酷繪本技術團隊開發出新的解壓方式——ZIP 流式解壓技術,併成功應用在優酷繪本秒開專案中,30M+ 繪本平均載入時長只需 0.91s,載入耗時比傳統的解壓方式降低了 88.3%,讓使用者的閱讀體驗直線提升。
本文將介紹 ZIP 流式解壓的原理和技術實現路徑,希望為大家帶來啟發,將 ZIP 流式解壓技術更多的應用到業務中。
一 什麼是ZIP
ZIP 是一種檔案格式,定義瞭如何將多個檔案、資料塊組織在一起形成一個完整的檔案。例如我們常見的 .apk,.ipa,.sketch,都是ZIP檔案。通常程式是這樣建立 ZIP 檔案的:
- 壓縮單個檔案形成單檔案資料塊;
- 在資料塊前後新增檔案描述資訊;
- 對每個待壓縮的檔案重複以上步驟後,拼接所有資料形成更大的資料塊;
提取所有檔案描述資訊,生成一份「檔案目錄」,附在最後一個資料塊的尾部。
我們將檔案前部描述資訊稱為 Local File Header,檔案後部描述資訊稱為 Data Descriptor, 被壓縮的檔案本身稱為 File Data,將最後的檔案目錄稱為 Central Directory。以上所有合在一起,就是一個標準的 ZIP 檔案。如下圖:
一個標準的解壓方式總是從讀取 ZIP 檔案末尾開始的,我們以解壓上圖的 File Data 1 為例:
- 首先在 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 去解壓檔案資料,其解壓流程就可以變為:
- 從頭開始,搜尋到 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 的描述,畫出其二進位制檔案中的排列:
其中的關鍵資訊為:
後設資料簽名是一個 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 操作進行加密。其解密方式也是一樣的。
這種方式也支援流式解密。
兩種常用的加密方式都支援流式解密,那麼加解密需要的關鍵資訊,在 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 的相關資訊就存放在這裡。其關鍵資訊如下:
原來壓縮演算法被藏到了 Extra Data 中。那麼鹽值被存放在哪裡了?答案是存放在 File Data 的頭尾。
綜上,我們找到解密所需的所有關鍵資訊,整個流式解密解壓的所有技術點都被我們探索完。剩下的便是按原理實現,以及細節的打磨。
三 總結
說了那麼多,流式解壓究竟有什麼價值呢?
由於流式解壓實現了邊下載邊解壓,將整個操作的時長從下載 + 解壓縮變成了約等於純下載的時長,直接抹掉了解壓的耗時。在 39.1 MB 大小的 ZIP 包下載解壓測試中,耗時從 9.08 秒降低至 4.17 秒,有將近 100% 的提速!同時,你可以不必等待整個 ZIP 下載解壓完,而是在解壓完一小部分資料的時候,就直接展示 UI。使用者側看起來就好像一瞬間就解壓完了。
因此,流式解壓可以應用在許多時間敏感的操作裡,也可以用來最佳化基於 ZIP 檔案的相關業務。例如基於 ZIP 的全域性換膚加速、基於 ZIP 的 Web 資源快取載入的加速等等。前言中的優酷繪本秒開就是基於這一技術實現。
參考
[1]
[2]AES Encryption Information: Encryption Specification AE-1 and AE-2
[3]ZIP File Format Specification
[4]AES Coding Tips for Developers
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31550522/viewspace-2718633/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- java解壓rar,解壓zipJava
- Java實現多檔案邊壓縮邊下載Java
- Android 下載Zip檔案,並解壓到本地Android
- linux 解壓rar,zipLinux
- Mac 解密 zip 解壓Mac解密
- 影片壓縮技術助力邊防影片監控
- CentOS中zip壓縮和unzip解壓縮命令詳解CentOS
- Ashampoo ZIP Pro 4,解壓縮
- 新手教程:如何在 Linux 下解壓 Zip 檔案Linux
- WinZip Pro 9 for Mac 專業zip壓縮解壓工具Mac
- linux常用壓縮解壓複製下載命令Linux
- 使用zlib庫解壓zip檔案
- nodejs解壓zip/rar檔案到本地,並獲取到解壓進度NodeJS
- Mac系統下的zip壓縮包解壓到Windows下出現亂碼的解決方法MacWindows
- linux下解壓部分zip檔案存在亂碼解決辦法Linux
- Linux 解壓zip檔案詳解之unzip命令!Linux
- Linux科研武器庫 - 檔案壓縮與解壓縮 - zip / unzipLinux
- rar壓縮解壓工具:RAR Extractor - ZIP Unarchiver中文啟用版Hive
- p7zip 解壓超過 4G壓縮包
- 「Python實用祕技01」複雜zip檔案的解壓Python
- linux下壓縮解壓縮命令Linux
- linux下壓縮、解壓命令大全Linux
- Golang 學習筆記(五)- archive/zip 實現壓縮及解壓Golang筆記Hive
- The Unarchiver - Unzip RAR ZIP Mac - mac解壓縮工具HiveMac
- 雲伺服器:解決linux下zip檔案解壓亂碼問題伺服器Linux
- 邊下載邊播放的播放器Android邊下邊播播放器Android
- Laravel 中建立 Zip 壓縮檔案並提供下載Laravel
- Linux下的tar壓縮解壓縮命令詳解Linux
- 使用java API進行zip遞迴壓縮資料夾以及解壓JavaAPI遞迴
- zip壓縮檔案處理方案(Zip4j壓縮和解壓)
- linux 下的解壓Linux
- Linux下檔案的壓縮與解壓Linux
- linux 下壓縮與解壓資料夾Linux
- Linux壓縮解壓Linux
- CentOS 壓縮解壓CentOS
- java 壓縮包 遍歷解壓 zip 和 7z 指定格式檔案Java
- 快速解壓 Mac上zip 檔案的兩種方法Mac
- 深耕邊緣計算 揭秘阿里雲邊緣雲網一體化的技術實踐阿里