<form id="dlljd"></form>
        <address id="dlljd"><address id="dlljd"><listing id="dlljd"></listing></address></address>

        <em id="dlljd"><form id="dlljd"></form></em>

          <address id="dlljd"></address>
            <noframes id="dlljd">

              聯系我們 - 廣告服務 - 聯系電話:
              您的當前位置: > 關注 > > 正文

              天天最資訊丨Zip格式結構圖總覽 Zip文件結構詳解

              來源:CSDN 時間:2023-03-17 10:28:08

              【前言】


              (資料圖片僅供參考)

              一、Zip格式結構圖總覽

              二、Zip文件結構詳解

              zip格式壓縮包主要由三大部分組成:數據區、中央目錄記錄區(也有叫核心目錄記錄)、中央目錄記錄尾部區

              1、數據區

              數據區是由一系列本地文件記錄組成,本地文件記錄主要是記錄了壓縮前后文件的元數據以及存放壓縮后的文件,組成部分也分為三大部分:本地文件頭、文件數據、文件描述1.1、本地文件頭本地文件頭主要是記錄了壓縮文件的元數據: 1)0~3:4個字節,用來存放本地文件頭標識:0x04034b50,用于解壓時候,讀取判斷文件頭的開始 2)4~5:2個字節,記錄解壓縮文件所需的最低支持的ZIP規范版本,apk壓縮版本默認是20, 即Deflate壓縮方式

              3)6~7:2個字節,記錄通用標志位,第0位為1時(即二進制:00000000 00000001),表示文件被加密,解壓時候需要解密;第3位為1時候(即二進制:00000000 00000100),表示有數據描述部分,那么本地文件頭中的 CRC-32、壓縮大小和未壓縮大小字段都被設置為0(雖然zip規范是這么定義,但是發現有些壓縮包即使聲明有數據描述部分,但是本地文件頭的CRC-32、壓縮大小和未壓縮大小依然還是設置為真實值) , 正確的值被放在緊跟在壓縮數據之后的數據描述部分,apk的通用標志位默認傳0即可,也有傳2048、2056,目前第15位是PKWARE保留位,沒啥意義,更多通用標志位含義可見這里 4)8~9:2個字節,記錄壓縮包所用到的壓縮方式,apk默認Deflate壓縮,傳8即可, 要是傳0,則是不壓縮,各種壓縮方式對應數值如下:

              5)10~11:2個字節,記錄文件最后修改時間,是MS-DOS格式編碼的時間 6)12~13:2個字節,記錄文件最后修改日期,是MS-DOS格式編碼的日期 7)14~17:4個字節,記錄文件未壓縮時的CRC-32校驗碼8)18~21:4個字節,記錄文件壓縮后的大小9)22~25:4個字節,記錄文件未壓縮的大小10)26~27:2個字節,記錄文件名的長度(假設文件名長度為n) 11)28~29:2個字節,記錄擴展區的長度(假設擴展區長度為m) 12)30~30+n: n個字節,記錄文件名13)30+n~30+n+m: m個字節,記錄擴展數據1.2、文件數據文件數據緊跟在本地文件頭之后,一般是壓縮后的文件數據或壓縮方式選擇不壓縮時候,用來存儲未壓縮文件數據。 1.3、文件描述文件描述符僅在通用位標志的第 3 位被設置為1時才存在。 它是字節對齊的,緊跟在文件數據的最后一個字節之后。當且僅當無法在 .ZIP 文件中查找時才使用此描述符,例如:當輸出 的.ZIP 文件是標準輸出或不可查找設備時使用文件描述,換句話說,正常情況下都不需要使用 數據描述符標識不一定有,因為一開始規范是沒有的,后面才加上去的

              2、中央目錄記錄區(也稱核心目錄記錄區 )

              中央目錄記錄區是有一系列中央目錄記錄所組成,一條中央目錄記錄對應數據區中的一個壓縮文件記錄,中央目錄記錄由以下部分構成: 1)0~3:4個字節,記錄核心目錄文件頭標識:0x02014b50,用于解壓時候,查找判斷是否是中央目錄的開始位置 2)4~5:2個字節,記錄壓縮所用的版本,同數據區本地文件頭的解壓所需版本,apk設置203)6~7:2個字節,記錄解壓所需的最小版本,同數據區本地文件頭的解壓所需版本,apk設置204) 8~9:2個字節,通用位標記,同數據區本地文件頭的通用位標記 5)壓縮方法、文件最后修改時間、文件最后修改日期、CRC-32校驗碼、壓縮后大小、未壓縮大小、文件名長度、擴展區長度,這幾個字段的含義都等同于數據區本地文件頭對應字段的含義 6)32~33:2個字節,記錄文件注釋的長度7)34~35:2個字節,記錄文件開始位置的磁盤編號,一般傳0即可 8)內部文件屬性、外部文件屬性,一般也是傳0即可 9)42~45:4個字節,記錄數據區本地文件頭相對于壓縮包開始位置的偏移量

              3、中央目錄記錄尾部區

              中央目錄記錄尾部主要作用是用來定位中央目錄記錄區的開始位置,同時記錄壓縮包的注釋內容

              1)0~3:4個字節,中央目錄記錄尾部開頭標記:0x06054b50,用于解壓時,查找判斷中央目錄尾部的起始位置 2)4~5:2個字節,記錄中央目錄記錄尾部區所在磁盤編號3)6~7:2個字節,記錄中央目錄開始位置所在的磁盤編號4)8~9:2個字節,該磁盤上所記錄的核心目錄數量5)10~11:2個字節,zip壓縮包中的文件總數6)12~15:4個字節,整個中央目錄的大小(以字節為單位)7)16~19:4個字節,中央目錄開始位置相對位移8)20~21:2個字節,注釋內容的長度(假設長度為n) 9)22~22+n:n個字節,注釋內容

              三、壓縮包解壓過程

              1、先從中央目錄尾部區著手,目標是找到中央目錄尾部開頭標記:0x06054b50,從上述對zip壓縮包結構分析可知,中央目錄尾部區除了注釋內容之外,固定大小占22個字節,那么假如注釋內容為空的時候,將指針從文件尾部往前移動22個字節,然后讀取4個字節的數據,就正好是中央目錄尾部開頭標記:0x06054b50,但是注釋內容是否為空在實際操作中是不可得知的,所以只能設置一個循環,每次遞增一個字節,不斷推測注釋內容的長度,又因為注釋長度用2個字節表示,那么注釋長度最大只能是65535個字節,所以可以在0~65535這個范圍內不斷推測注釋內容的長度. 下面是java代碼實現的查找中央目錄尾部開始位置的案例:

              /**     * 查找中央目錄結尾的開始位置     * @param zipContents     * @return     */    private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {//判斷是否小端模式排列        assertByteOrderLittleEndian(zipContents);        int archiveSize = zipContents.capacity();        //由于核心目錄尾部大小至少是22個字節,小于22就是沒意義的        if (archiveSize < 22) {return -1;        }        //注釋內容長度只可能是: 【壓縮包大小 - 核心目錄尾部固定大?。?2字節)】與 【注釋內容最大長度】中的最小值        int maxCommentLength = Math.min(archiveSize - 22, 65535);        //假如沒有注釋內容,那么核心目錄尾部開始位置是:壓縮包大小 -22        int eocdWithEmptyCommentStartPosition = archiveSize - 22;        // 循環查找,假設沒有注釋內容到每次遞增一個字節的注釋內容,查找出:核心目錄結尾標識0x06054b50        for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength; expectedCommentLength++) {int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength;            // 核心目錄結尾標志:0x06054b50(十進制為:101010256), 標志位長度為4個字節,int類型剛好4字節            // zipContents.getInt(eocdStartPos),即從eocdStartPos位置開始讀取4個字節的內容            if (zipContents.getInt(eocdStartPos) == 101010256) {//核心目錄結尾標志的開始位置偏移20個字節就是注釋內容長度,因為注釋內容長度是2個字節,對應就是short類型的大小                int actualCommentLength = getUnsignedInt16(zipContents, eocdStartPos + 20);                // 要是從壓縮包中讀取到的注釋長度跟循環查找計算出的注釋長度一致,那么就是找到了確切的核心目錄結尾標記的開始位置了                if (actualCommentLength == expectedCommentLength) {return eocdStartPos;                }            }        }        return -1;    }static void assertByteOrderLittleEndian(ByteBuffer buffer) {if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {throw new IllegalArgumentException("ByteBuffer byte order must be little endian");        }    }

              2、中央目錄尾部開始位置找到之后,那么可以從中獲取到中央目錄的開始位置,中央目錄的大小,以及中央目錄記錄條目總數3、接著,又可以從中央目錄中讀取到本地文件頭相對壓縮包開始位置的偏移量,那么就能讀到本地文件記錄,并從中解壓出文件數據,大概的解壓流程就到此結束啦

              【擴展知識】

              剛才在java舉例中有涉及到一個小端排序問題,因為在Jvm中默認都是按大端模式儲存, 而 .ZIP格式的數據是按小端模式編排的,所以需要手動對ByteBuffer中的數據進行小端排序,那么,什么是小端模式,什么是大端模式呢?

              1、大端模式:Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端,大端模式是跟人讀寫習慣是一致的,比如:數字0x12345678 與 0x11223344,大端模式表示如下:

              低地址 ----------------------------------------------------> 高地址0x12  |  0x34  |  0x56  |  0x78 | 0x11  |  0x22  |  0x33  |  0x44

              2、小端模式:Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端,比如:數字0x12345678 與 0x11223344,小端模式表示如下:

              低地址 ----------------------------------------------------> 高地址0x78  |  0x56  |  0x34  |  0x12 | 0x44  |  0x33  |  0x22  |  0x11

              那么,為啥會存在大小端不統一的問題呢?

              既然大小端都有存在的必要性,那大小端模式各有啥優勢呢?

              【注意】字符是只有1個字節,故對于字符不存在大小端模式之分,只有大于1個字節的才分大小端模式

              【實踐案例】

              理論說了一大篇幅,想必各位看官已是頭昏腦漲,咱們來動手分析一個壓縮包看看,是否如咱們理論所言那般,下面是一個安卓安裝包(.apk)的案例: 1、首先,先找到中央目錄結尾標志:0x06054b50,因為zip格式是小端模式,那么,咱們看到的應該是:50 4B 05 06, 用010 Editor打開apk,成功查找到中央目錄結尾標志從截圖可以看到: 1)當前磁盤編號為:0x0000(即十進制:0) 2)中央目錄開始位置的磁盤編號也是:0x0000(即十進制:0), 3)該磁盤上所記錄的中央目錄數量:0xD236(轉為大端模式就是0x36D2,十進制:14034) 4)zip壓縮包中的文件總數:0xD236(轉為大端模式就是0x36D2,十進制:14034) 5)中央目錄大?。?x7A1E1600(轉為大端模式就是0x00161E7A,十進制:1449594), 6)中央目錄開始位置的相對位移:0x2A03480C(轉為大端模式就是0x0C48032A,十進制:206045994) 7)注釋長度:0x0000(即長度為0) 2、從第一步中,咱們可以知道中央目錄開始位置是在地址206045994,那么查一下這個地址: 從截圖可以看到 ① 從地址206045994開始讀取4個字節,得到0x504B0102, 按大端模式排序為:0x02014b50, 剛好就是前面提到的中央目錄文件頭標識② 壓縮所用版本:0x1400(轉為大端模式就是0x0014,十進制:20) ③ 解壓所需版本:0x1400(轉為大端模式就是0x0014,十進制:20) ④ 通用位標記:0x0808(十進制:2056, 那么就是第3位設置為1,說明數據區有文件描述符) ⑤ 壓縮方法:0x0800(轉為大端模式就是0x0008,十進制:8) ⑥ 文件最后修改時間:0x4B79(轉為大端模式就是0x794B,二進制:?0111100101001011?)

              按照上面的MS-DOS時間編碼規則,對二進制01111 001010 01011?進行 拆分計算,時:01111(十進制:15),分:001010(十進制:10),秒:01011(十進制:11,這是秒除以2的值,故實際秒為11 * 2 = 22),那么,文件的最后修改時間為:15:10:22⑦ 文件最后修改日期:0xE552(轉為大端模式就是0x52E5,二進制:?0101001 0111 00101??),年:0101001(十進制:41,1980 + 41 = 2021),月: 0111(十進制:7),日:00101(十進制:5),那么文件的最后修改日期為:2021-7-5,比對一下跟壓縮軟件的結果是一致的 ⑧ CRC-32校驗碼:0x04D127C5(轉為大端模式就是0xC527D104),跟上述壓縮軟件結果也是一致的 ⑨ 壓縮后的大?。?x20E80900(轉為大端模式就是0x0009E820, 十進制:?649248,約為634.03KB) ⑩ 未壓縮的大?。?xA0D91B00(轉為大端模式就是0x001BD9A0, 十進制:??1825184?,約為1.74MB),跟上述壓縮軟件結果也是一致的 ? 文件名長度:0x1400(轉為大端模式就是0x0014,十進制:??20) ? 擴展區長度、文件注釋長度、文件開始位置的磁盤編號、內部文件屬性都是:0x0000 ? 外部文件屬性、本地文件頭的相對位移都是:0x00000000 ? 文件名:0x4D 45 54 41 2D 49 4E 46 2F 4D 41 4E 49 46 45 53 54 2E 4D 46, 這些是字符ASCII碼,轉為字符是:META-INF/MANIFEST.MF

              【注意】假如文件名有中文的話,那這里存放UTF-8編碼數據,中文一般先轉換為Unicode編碼字符,然后用UTF-8編碼方式存儲(Unicode只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲, UTF-8是unicode的一種實現方式,unicode實現方式還有UTF-16和UTF-32)

              【UTF-8小知識】UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度。 UTF-8的編碼規則很簡單,只有2條:

              1??對于單字節的符號,字節的第1位(字節的最高位)設為0,后面7位為這個符號的unicode碼。因此對于英語字母,UTF-8編碼和ASCII碼是相同的。 2??對于n字節的符號(n>1),第1個字節的前n位都設為1,第n+1位設為0,后面字節的前兩位一律設為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼 比如:已知“嚴”的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第3行的范圍內(0000 0800-0000 FFFF),因此“嚴”的UTF-8編碼需要3個字節,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,從“嚴”的最后1個二進制位開始,依次從后向前填入格式中的x,多出的位補0。這樣就得到了“嚴”的UTF-8編碼是“11100100 10111000 10100101”,轉換成十六進制就是E4B8A5

              ? 因為擴展區長度為0,所以文件名后面緊跟壓縮之后的文件數據,由上面分析的壓縮長度為649248,所以后面649248個字節的數據都是文件數據

              ? 因為是本地文件頭的通用標志位第3位設置為1,所以存在數據描述區,數據描述區標識:0x504B0708(轉換為大端模式:0x08074b50)

              ? 數據描述符中的CRC-32校驗碼、壓縮大小、未壓縮大小跟本地文件頭的值一致

              責任編輯:

              標簽:

              相關推薦:

              精彩放送:

              新聞聚焦
              Top 中文字幕在线观看亚洲日韩