48

難以附加到ObjectOutputStream嗎?追加到ObjectOutputStream

我想附加到對象列表。以下代碼片段是一項任務完成後調用的函數。

FileOutputStream fos = new FileOutputStream 
      (preferences.getAppDataLocation() + "history" , true); 
ObjectOutputStream out = new ObjectOutputStream(fos); 

out.writeObject(new Stuff(stuff)); 
out.close(); 

但是,當我嘗試讀取它時,我只得到文件中的第一個。 然後我得到java.io.StreamCorruptedException

要讀我使用

FileInputStream fis = new FileInputStream 
     (preferences.getAppDataLocation() + "history"); 
ObjectInputStream in = new ObjectInputStream(fis);  

try{ 
    while(true) 
     history.add((Stuff) in.readObject()); 
}catch(Exception e) { 
    System.out.println(e.toString()); 
} 

我不知道該怎麼雖然沒有例外許多對象將出席,所以我讀書。從谷歌說這是不可能的。我想知道如果有人知道一種方式?

+0

你是否從流中獲得任何東西,還是第一次在循環中拋出異常? – skaffman 2009-07-28 14:53:58

+0

它讀取我保存的第一個對象,然後我得到異常。 – 2009-07-28 15:02:07

回答

67

這裏的竅門:子類ObjectOutputStream並覆蓋writeStreamHeader方法:

public class AppendingObjectOutputStream extends ObjectOutputStream { 

    public AppendingObjectOutputStream(OutputStream out) throws IOException { 
    super(out); 
    } 

    @Override 
    protected void writeStreamHeader() throws IOException { 
    // do not write a header, but reset: 
    // this line added after another question 
    // showed a problem with the original 
    reset(); 
    } 

} 

要使用它,只是檢查歷史文件是否存在,並實例可以在本追加流(如果該文件存在=我們append =我們不需要頭)或原始流(如果文件不存在=我們需要頭)。

編輯

我很不滿意類的第一個命名。這一個更好的:它描述了「什麼它是」寧可然後「它是如何做」

編輯

更名一次,澄清,這個流只適用於附加到現有的文件。它不能用於創建具有對象數據的新文件

編輯

添加了調用reset()this question後發現,僅僅推翻writeStreamHeader原始版本是一個無操作可能在某些情況下創建無法讀取流。

7

由於序列化文件的確切格式,追加確實會破壞它。您必須將所有對象作爲同一個流的一部分寫入該文件,否則當它在等待對象時讀取流元數據時會崩潰。

您可以閱讀Serialization Specification瞭解更多詳情,或者(更容易)閱讀this thread,其中Roedy Green基本上是這麼說的。

13

正如API所述,構造函數ObjectOutputStream將序列化流頭寫入基礎流。預計這個頭文件只會在文件的開始處出現一次。因此調用

new ObjectOutputStream(fos); 

上指的是同一個文件FileOutputStream多次將多次和損壞文件寫頭。

5

避免此問題的最簡單方法是在寫入數據時保持OutputStream打開,而不是在每個對象之後關閉它。調用reset()可能是可取的,以避免內存泄漏。

另一種方法是將文件作爲一系列連續的ObjectInputStreams讀取。但是這需要你記住你讀的字節數(這可以用FilterInputStream實現),然後關閉InputStream,再次打開它,跳過那麼多字節,然後把它包裝在ObjectInputStream()中。

0

如何在每次添加對象之前,讀取並複製文件中的所有當前數據,然後將所有數據一起覆蓋文件。