如何附加到现有的java.io.ObjectStream?

至于现在,当我尝试追加一个Object时,我会得到java.io.StreamCorruptedException 。 我在互联网上搜索了一种克服这种情况的方法。 到目前为止我找到的答案是无法完成的。 解决此问题的方法是将对象写入列表,然后将列表写入文件。

但是每次添加新对象时我都要覆盖该文件。 它似乎不是加class的最佳解决方案。

有没有办法将对象附加到现有的对象流?

它实际上很容易做到。 当您要添加到现有流时,您需要使用ObjectOutStream的子类来覆盖writeStreamHeader以便不在文件中间写入第二个标头。 例如

 class NoHeaderObjectOutputStream extends ObjectOutputStream { public NoHeaderObjectOutputStream(OutputStream os) { super(os); } protected void writeStreamHeader() {} } 

然后只需使用标准的ObjectInputStream来读取整个文件。

我在这个主题上找到的最好的文章是: http : //codify.flansite.com/2009/11/java-serialization-appending-objects-to-an-existing-file/

覆盖ObjectOutputStream的“解决方案”完全错误。 我刚刚调查了由此造成的错误(浪费了两个宝贵的日子)。 它不仅有时会破坏序列化文件,而且甚至设法读取而不抛出exception并最终提供垃圾数据(混合字段)。 对于那些难以置信的人,我附上了一些暴露问题的代码:

 import java.io.*; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) throws Exception { File storageFile = new File("test"); storageFile.delete(); write(storageFile, getO1()); write(storageFile, getO2()); write(storageFile, getO2()); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(storageFile)); read(ois, getO1()); read(ois, getO2()); read(ois, getO2()); } private static void write(File storageFile, Map o) throws IOException { ObjectOutputStream oos = getOOS(storageFile); oos.writeObject(o); oos.close(); } private static void read(ObjectInputStream ois, Map expected) throws ClassNotFoundException, IOException { Object actual = ois.readObject(); assertEquals(expected, actual); } private static void assertEquals(Object o1, Object o2) { if (!o1.equals(o2)) { throw new AssertionError("\n expected: " + o1 + "\n actual: " + o2); } } private static Map getO1() { Map nvps = new HashMap(); nvps.put("timestamp", "1326382770000"); nvps.put("length", "246"); return nvps; } private static Map getO2() { Map nvps = new HashMap(); nvps.put("timestamp", "0"); nvps.put("length", "0"); return nvps; } private static ObjectOutputStream getOOS(File storageFile) throws IOException { if (storageFile.exists()) { // this is a workaround so that we can append objects to an existing file return new AppendableObjectOutputStream(new FileOutputStream(storageFile, true)); } else { return new ObjectOutputStream(new FileOutputStream(storageFile)); } } private static class AppendableObjectOutputStream extends ObjectOutputStream { public AppendableObjectOutputStream(OutputStream out) throws IOException { super(out); } @Override protected void writeStreamHeader() throws IOException { // do not write a header } } } 

如该文章所述,您可以使用以下解决方案之一:

解决方案#1:在单个流中伪造多个文件

将您的“事务”写入ByteArrayOutputStream,然后通过DataOutputStream将此ByteArrayOutputStream的长度和内容写入文件。

解决方案#2:重新打开并跳过

另一个解决方案涉及使用以下方

 long pos = fis.getChannel().position(); 

关闭文件,重新打开文件,然后在读取下一个事务之前跳到此位置。

非常感谢George Hategan揭露代码的问题。 我也检查了一会儿。 然后,它击中了我。 如果您使用子类ObjectOutputStream并重写writeStreamHeader()方法来写入数据,则必须使用并行子类ObjectInputStream并重写readStreamHeader()方法来读取数据。 当然,我们可以在写入和读取对象的不同实现之间进行锯齿形,但只要我们在写入/读取过程中使用相应的子类对 – 我们(希望)就可以了。 汤姆。

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; public class SerializationDemo { public static void main(String[] args) throws Exception { File storageFile = new File("test.ser"); storageFile.delete(); write(storageFile, getO1()); write(storageFile, getO2()); write(storageFile, getO2()); FileInputStream fis = new FileInputStream(storageFile); read(fis, getO1()); read(fis, getO2()); read(fis, getO2()); fis.close(); } private static void write(File storageFile, Map o) throws IOException { ObjectOutputStream oos = getOOS(storageFile); oos.writeObject(o); oos.flush(); oos.close(); } private static void read(FileInputStream fis, Map expected) throws ClassNotFoundException, IOException { Object actual = getOIS(fis).readObject(); assertEquals(expected, actual); System.out.println("read serialized " + actual); } private static void assertEquals(Object o1, Object o2) { if (!o1.equals(o2)) { throw new AssertionError("\n expected: " + o1 + "\n actual: " + o2); } } private static Map getO1() { Map nvps = new HashMap(); nvps.put("timestamp", "1326382770000"); nvps.put("length", "246"); return nvps; } private static Map getO2() { Map nvps = new HashMap(); nvps.put("timestamp", "0"); nvps.put("length", "0"); return nvps; } private static ObjectOutputStream getOOS(File storageFile) throws IOException { if (storageFile.exists()) { // this is a workaround so that we can append objects to an existing file return new AppendableObjectOutputStream(new FileOutputStream(storageFile, true)); } else { return new ObjectOutputStream(new FileOutputStream(storageFile)); } } private static ObjectInputStream getOIS(FileInputStream fis) throws IOException { long pos = fis.getChannel().position(); return pos == 0 ? new ObjectInputStream(fis) : new AppendableObjectInputStream(fis); } private static class AppendableObjectOutputStream extends ObjectOutputStream { public AppendableObjectOutputStream(OutputStream out) throws IOException { super(out); } @Override protected void writeStreamHeader() throws IOException { // do not write a header } } private static class AppendableObjectInputStream extends ObjectInputStream { public AppendableObjectInputStream(InputStream in) throws IOException { super(in); } @Override protected void readStreamHeader() throws IOException { // do not read a header } } } 

您需要创建一个新的ObjectInputStream来匹配每个ObjectOutputStream 。 我不知道将状态从完整的ObjectInputStream传递到ObjectOutputStream (没有完整的重新实现,这在纯Java中有点棘手)。