是否无法附加到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 :(得分:73)
这是诀窍:子类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();
}
}
要使用它,只需检查历史文件是否存在,并实例化此可附加流(如果文件存在=我们追加=我们不想要标题)或原始流(如果文件存在不存在=我们需要一个标题)。
修改强>
我对班级的第一个命名感到不满意。这个更好:它描述了“它是什么”而不是“如何完成”
修改强>
再次更改名称,以澄清此流仅用于附加到现有文件。它不能用于创建包含对象数据的 new 文件。
修改强>
在this question显示刚刚覆盖reset()
为无操作的原始版本在某些条件下创建无法读取的流后,添加了对writeStreamHeader
的调用
答案 1 :(得分:13)
正如API所说,ObjectOutputStream
构造函数将序列化流标头写入基础流。并且该头文件在文件的开头只有一次。所以打电话
new ObjectOutputStream(fos);
引用同一文件的FileOutputStream
多次将多次写入标题并破坏文件。
答案 2 :(得分:7)
由于序列化文件的格式准确,追加确实会破坏它。您必须将所有对象作为同一个流的一部分写入文件,否则当它在期望对象时读取流元数据时会崩溃。
您可以阅读Serialization Specification了解更多详情,或者(更简单)阅读this thread,其中Roedy Green基本上说的就是我刚才所说的。
答案 3 :(得分:5)
避免此问题的最简单方法是在编写数据时保持OutputStream处于打开状态,而不是在每个对象之后关闭它。可能建议调用reset()
以避免内存泄漏。
另一种方法是将文件读作一系列连续的ObjectInputStreams。但是这需要你继续计算你读取的字节数(这可以通过FilterInputStream实现),然后关闭InputStream,再次打开它,跳过那么多字节,然后将它包装在ObjectInputStream()中。
答案 4 :(得分:0)
每次附加对象之前,读取并复制文件中的所有当前数据,然后将所有数据一起覆盖到文件中。
答案 5 :(得分:0)
我已经扩展了公认的解决方案,以创建一个可用于附加和创建新文件的类。
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class AppendableObjectOutputStream extends ObjectOutputStream {
private boolean append;
private boolean initialized;
private DataOutputStream dout;
protected AppendableObjectOutputStream(boolean append) throws IOException, SecurityException {
super();
this.append = append;
this.initialized = true;
}
public AppendableObjectOutputStream(OutputStream out, boolean append) throws IOException {
super(out);
this.append = append;
this.initialized = true;
this.dout = new DataOutputStream(out);
this.writeStreamHeader();
}
@Override
protected void writeStreamHeader() throws IOException {
if (!this.initialized || this.append) return;
if (dout != null) {
dout.writeShort(STREAM_MAGIC);
dout.writeShort(STREAM_VERSION);
}
}
}
此类可用作ObjectOutputStream的直接扩展替代。 我们可以使用以下类:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ObjectWriter {
public static void main(String[] args) {
File file = new File("file.dat");
boolean append = file.exists(); // if file exists then append, otherwise create new
try (
FileOutputStream fout = new FileOutputStream(file, append);
AppendableObjectOutputStream oout = new AppendableObjectOutputStream(fout, append);
) {
oout.writeObject(...); // replace "..." with serializable object to be written
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}