第五章、epub文件處理 -- 解壓epub文件

第五章、epub文件處理 -- 解壓epub文件

 

本章將介紹程序是如何將epub內部被壓縮過的xml文件轉化爲一個能夠正常解析的char數組。要想將文件轉換成char數組,就須要一個字符流類。而專門針對epub內部被xml文件的字符流類須要一個專門的字節流類ZLXMLParser類。獲取這種字節流的工做就是由經過ZLZipEntryFile類的getInputStream方法來完成的。 html

 

在詳細介紹解壓流程以前,咱們先簡略介紹一下zip文件(epub文件與zip文件使用的是用一種壓縮格式)的解壓原理。 java

zip文件內部的文件和文件夾會有一個頭信息(Header)。頭信息(Header)由特定的標示符描述,文件的標示符是0x04034b50,文件夾的標示符是0x02014b50 c++

緊跟在頭信息以後的是文件或文件夾的元信息。元信息中包括「壓縮狀態下大小」(Compressed size)、「解壓縮狀態下大小」(Uncompressed size)、和「文件名」(FileName)等信息。而咱們能夠經過讀取epub文件的字節流獲得epub文件內部每一個xml文件的元信息。 數組

有了這些元信息以後,當咱們須要解析zip文件內部中某一個xml文件時,而後咱們能夠利用jni調用c語言寫的zlib庫,將epub文件內部xml文件對應的部分解壓成能夠正常解析的xml文件字節流。 函數

 

以上就是大體的解壓原理,若是你們還想再進一步瞭解相關信息,有兩個網址能夠推薦你們讀下。 編碼

http://blog.sina.com.cn/s/blog_4c3591bd0100zzm6.html spa

http://blog.csdn.net/zhoudaxia/article/details/8034606 .net

http://www.cnblogs.com/esingchan/p/3958962.html code

 

本章中涉及的主要方法有: xml

ZLZipEntryFile類:實現了getInputStream方法

ZipFile類內部InputStreamHolder接口實現類:得到epub文件的字節流

LocalFileHeader類:這個類記錄了epub文件內部的xml文件具體從epub文件字節流哪一個位置開始,以及在字節流中佔據多少字節。

ZipInputStream類:這個覆寫了InputStream類的read方法,這個方法將調用DeflatingDecompressor類。

DeflatingDecompressor類:這個類調用語言寫的zlib庫,將epub文件字節流中的一部分解壓還原成正常的xml文件字節流。


下面咱們就開始詳細介紹代碼:


ZLZipEntryFile類的getInputStream方法

getInputStream方法調用了兩個方法:

ZLZipEntryFile類的getZipFile方法,參數myParent是一個表明epub文件的ZLPhysicalFile

ZipFile類的getInputStream方法,參數myName是一個表明epub內部xml文件的文件名(參考第二章「解析資源文件」)

    

    ZLZipEntryFile類的getZipFile方法

這個方法返回了一個ZipFile類,並實現了ZipFile類內的InputStreamHolder接口的getInputStream方法(52行)

由於getZipFile方法中的file參數是一個表明epub文件的ZLPhysicalFile類,因此這個getInputStream方法會返回一個FileInputStream(參考第二章「解析資源文件」)

這個方法最終會返回了一個ZipFile類(63行)

            

    ZipFile類的getInputStream方法

這個方法調用了同一個類內的getHeader方法

          



ZipFile類的getHeader方法

這個方法是要得到一個LocalFileHeader類。這個LocalFileHeader類記錄了epub文件內部的xml文件具體從epub文件字節流哪一個位置開始,壓縮狀態下佔據多少字節以及解壓狀態下佔據多少字節

LocalFileHeader類中的「DataOffset」屬性記錄了epub內部xml文件表明了從字節流的哪一個位置開始,「CompressedSize」屬性記錄了這個xml文件在壓縮狀態下在字節流中佔據了多少字節,「UncompressedSize」屬性則記錄了xml文件在解壓狀態下會佔據多少字節

    


getHeader方法調用了兩個方法,ZipFile類的getBaseStream方法與ZipFile類的readFileHeader方法


    ZipFile類的getBaseStream方法

這個方法調用了MyBufferedInputStream類的構造函數,構造函數中又調用了ZipFile類內的InputStreamHolder接口實現類的getInputStream方法


InputStreamHolder接口的實現類在ZLZipEntryFile類的getZipFile方法中,咱們已經介紹過了,會返回一個FileInputStream類。myFileInputStream屬性就會指向這個FileInputStream類。

    


    ZipFile類的readFileHeader方法

這個方法會調用LocalFileHeader類裏面的readFrom方法


    LocalFileHeader類的readFrom方法

readFrom方法調用了MyBufferedInputStream類的read4Bytes方法

read4Bytes方法調用了四遍MyBufferedInputStream類的read方法

MyBufferedInputStream類的read方法則最終調用了MyBufferedInputStream類中myFileInputStream屬性指向的FileInputStream類的read方法(myFileInputStream屬性在MyBufferedInputStream類的構造函數中設置,剛纔已經介紹過)



    返回ZipFile類的readFileHeader方法

    代碼根據epub文件內部xml文件的名字取出對應的LocalFileHeader類以後,以此爲參數調用ZipFile類的createZipInputStream方法。這個方法帶又調用了一個ZipInputStream類的構造函數

    

返回ZipFile類的getHeader方法

LocalFileHeader類裏面的readFrom方法建立出來的LocalFileHeader類會被加入到myFileHeaders屬性指向的LinkedHashMap中。注意這裏LocalFileHeader類是以epub文件內部的xml文件的文件名(header.FileName)爲鍵名加入到LinkedHashMap裏去的。

 


返回ZLZipEntryFile類的getInputStream方法

代碼根據epub文件內部xml文件的名字取出對應的LocalFileHeader類以後,以此爲參數調用ZipFile類的createZipInputStream方法。這個方法帶又調用了一個ZipInputStream類的構造函數




ZipInputStream類的構造函數

MyBufferedInputStream類的setPosition方法經過LocalFileHeader類中的DataOffset屬性將epub文件的字節流定位到內部xml文件開始的位置

同時DeflatingDecompressor類的init方法則調用DeflatingDecompressor類的reset方法,將LocalFileHeader類中表明內部xml文件在在壓縮狀態下在epub字節流中佔據了多少字節,在解壓狀態下會佔據多少字節的信息分別賦給了DeflatingDecompressor類內的屬性。

    


    

如今咱們已經得到了ZipInputStream類,這個類其實就是一個針對epub文件內部被壓縮的xml文件的字節流類。咱們在這一章的開篇曾經講過:「要想將文件轉換成char數組,就須要一個字符流類。而專門針對epub內部xml文件的字符流類須要一個專門的字節流類做爲參數。」ZipInputStream就是這個專門的字節流類。如今咱們只須要在以特定字節流類爲參數,新建一個字符流類就完成任務了。而新建字符流類就是在ZLXMLParser類的構造函數中完成的。(ZLXMLParser類的調用流程請參見第六章)




下面,咱們再來介紹下,字符流類將壓縮的xml文件轉換成char數組的過程。

ZipInputStream類中read方法

咱們首先來複習下java中字節流類和字符流類的關係。程序在調用字符流類讀取文件的時候,其實底層仍是在調用字節流類在讀取。字節流現將文件轉換成byte數組,字符流得到這個byte數組以後再調用CharsetDecoder這個類,根據字符編碼將byte數組再轉換成相應的char數組。在字符流將壓縮的xml文件轉換成char數組的過程當中,CharsetDecoder這個類會根據utf-8的編碼將字節流轉換成字符流。

咱們須要關注的是FBReader是如何調用ZipInputStream類將壓縮的xml文件轉換成byte數組。這個其實就是ZipInputStream類中的read方法完成的事情。

ZipInputStream類的read方法會調用DeflatingDecompressor類的read方法填充做爲參數傳進來的空byte數組。



DeflatingDecompressor類的read方法中其實就是用myOutBuffer這個變量來填充傳進來的空byte數組。



那麼這個myOutBuffer這個變量又是在什麼地方被填充的呢?就是在DeflatingDecompressor類的另外一個無參read方法中。

這個無參read方法是在ZLXMLParser類的構造函數中被調用的,具體請參見第六章「epub文件處理 -- 解析 container文件與.opf文件」中關於讀取xml文件的三個核心類調用順序的介紹。



myOutBuffer屬性指向的byte數組是在DeflatingDecompressor類中的fillOutBuffer方法被填充的。


fillOutBuffer方法中的myStream屬性所指向的epub文件字節流已經在ZipInputStream類的構造函數中被定位到了內部xml文件開始的位置(請看對ZipInputStream構造函數的介紹)。



fillOutBuffer方法中,再調用MyBufferedInputStream類的read方法時,就會直接從內部xml文件開始的位置開始讀取,把xml文件壓縮狀態下所佔用的字節數所有讀取出來。

調用完MyBufferedInputStream類的read方法以後,經過jni調用的c++inflate方法的全部參數就都被設置好了:myInBuffer參數表明內部xml文件壓縮狀態下字節流轉換成的byte數組,myInBufferOffset參數爲0myInBufferLength參數表明內部xml文件壓縮狀態下字節流轉換成的byte數組的長度,myOutBuffer參數表明存放內部xml文件解壓狀態下字節流的byte數組。

c++inflate方法中,代碼調用了zlib庫的inflate方法。而後爲zlib庫裏的inflate方法設置了參數。



c++inflate方法調用完畢,myOutBuffer屬性指向的byte數組就填充完畢了。

字節流填充完byte數組以後,字符流會根據字符編碼再生成對應的char數組。整個流程就完成了。

相關文章
相關標籤/搜索