2010-09-09 165 views
3

我正試圖對應用程序實施文件壓縮。該應用程序已經存在了一段時間,所以它需要能夠讀取以前版本編寫的未壓縮文檔。我期望DeflateStream能夠處理一個未壓縮的文件,但是對於GZipStream,我得到了「GZip頭部中的幻數不正確」錯誤。對於DeflateStream,我會得到「解碼時發現無效數據」。我想它沒有找到標記文件的標題爲它的類型。可以使用DeflateStream或GZipStream來壓縮未壓縮的文件嗎?

如果不可能簡單地處理一個未壓縮的文件,那麼第二好的方法是確定一個文件是否被壓縮,然後選擇讀取文件的方法。我發現這個鏈接:http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/,但這是非常具體的實現,並不覺得正確的方法。它也可以提供誤報(我相信這很少見,但它確實表明這不是正確的做法)。

我考慮過的第三個選項是嘗試使用DeflateStream,如果發生異常,則回退到普通流IO。這也感覺混亂,並導致VS在異常中斷開(除非我解開異常,我不想這麼做)。

當然,我可能只是會以錯誤的方式去做。這是我在.Net 3.5中試過的代碼:

Stream reader = new FileStream(fileName, FileMode.Open, readOnly ? FileAccess.Read : FileAccess.ReadWrite, readOnly ? FileShare.ReadWrite : FileShare.Read); 

using (DeflateStream decompressedStream = new DeflateStream(reader, CompressionMode.Decompress)) 
{ 
    workspace = (Workspace)new XmlSerializer(typeof(Workspace)).Deserialize(decompressedStream); 

    if (readOnly) 
    { 
     reader.Close(); 
     workspace.FilePath = fileName; 
    } 
    else 
     workspace.SetOpen(reader, fileName); 
} 

任何想法?

謝謝! 盧克。

+0

如何使用不同的文件名或擴展名爲您的新文件格式? – Thilo 2010-09-09 10:24:29

+0

我想這會起作用(作爲一個標誌),但寧願避免poss,以便最終用戶不必知道他們的文檔有多個文件擴展名。我也有興趣知道是否有人有其他解決方案,只是爲了知道這是否是.Net的限制,或者我是否做錯了什麼。雖然謝謝! – Luke 2010-09-09 10:31:11

+0

「試圖確定」文件是否被壓縮(通過熵,檢查非ascii字符,無論如何)正在尋求麻煩。你*需要*一個正確的文件頭。壓縮整個文件意味着它不再是一個XML文檔(並且舊版本的應用程序會嘗試讀取它),所以沒有理由不能*添加標題:) – snemarch 2010-09-09 11:58:40

回答

1

您的文件格式是否有頭文件?如果不是,現在是是添加一個的時候(無論如何,你通過支持壓縮來改變文件格式)。選擇一個好的magic value,確保標題是可擴展的(添加一個版本字段,或爲特定版本使用特定的魔術值),然後就可以開始了。

加載後,檢查魔法值。如果不存在,請使用您當前的傳統加載例程。如果存在,標題會告訴你內容是否被壓縮。

更新

壓縮了該流意味着文件不再是一個XML文檔,因此沒有太多的理由期待文件不能包含您的數據更多。你真的想要一個標題標識你的文件:)

下面是例子(僞)代碼;我不知道.net是否有一個「子流」,SubRangeStream可能是你自己編碼的東西(DeflateStream可能會添加它自己的頭文件,所以子流可能不是必需的;可能會在後續變得更有用,雖然)。

Int64 oldPosition = reader.Position; 
reader.Read(magic, 0, magic.length); 
if(IsRightMagicValue(magic)) 
{ 
    Header header = ReadHeader(reader); 
    Stream furtherReader = new SubRangeStream(reader, reader.Position, header.ContentLength); 
    if(header.IsCompressed) 
    { 
     furtherReader = new DeflateStream(furtherReader, CompressionMode.Decompress); 
    } 

    XmlSerializer xml = new XmlSerializer(typeof(Workspace)); 
    workspace = (Workspace) xml.Deserialize(furtherReader); 
} else 
{ 
    reader.Position = oldPosition; 
    LegacyLoad(reader); 
} 

在現實生活中,我會做不同的事情有點 - 一些適當的錯誤處理和清理,例如。此外,我不會在IsRightMagicValue塊中直接使用新的加載程序代碼,而是基於魔術值(每個文件版本一個魔術值)分離出作品,或者我會保留一個「常見標題」部分與所有版本通用的字段。對於這兩種,我會使用Factory Method根據文件版本返回IWorkspaceReader

+0

我正在使用XML序列化,它有它自己的格式。 (是的,我在我的XML文檔中有一個版本號。)從第1天開始使用這個版本,所以我無法分辨文件是否被壓縮。這種方法也會遭受誤報,就像我的問題中鏈接的方法一樣。雖然謝謝! – Luke 2010-09-09 11:13:50

+0

@Luke:XML序列化使用*流*而不是*文件*。因此,您可以輕鬆打開*文件*並檢查標題,然後將XML文件*的子部分作爲XMLSerializer的*流*處理。 – snemarch 2010-09-09 11:36:00

+0

@Luke:檢查我的更新瞭解更多描述和一些(僞)代碼。 – snemarch 2010-09-09 11:56:11

1

難道你不能只是創建一個包裝類/函數來讀取文件和捕獲異常?像

try 
{ 
    // Try return decompressed stream 
} 
catch(InvalidDataException e) 
{ 
    // Assume it is already decompressed and return it as it is 
} 
+0

是的,這是我的問題中所述的選項3。這是我使用的,直到我找到一個更優雅的方法(假設有一個)。 – Luke 2010-09-09 11:23:05

+0

猜猜這取決於你定義的優雅。我發現這個文件比所有文件頭文件更優雅更簡單。特別是因爲你說你已經在使用XML編碼了。 – Svish 2010-09-10 19:17:56