Java 附加到 ObjectOutputStream
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1194656/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Appending to an ObjectOutputStream
提问by Hamza Yerlikaya
Is it not possible to append to an ObjectOutputStream
?
不能附加到ObjectOutputStream
?
I am trying to append to a list of objects. Following snippet is a function that is called whenever a job is finished.
我正在尝试附加到对象列表。以下代码段是一个在作业完成时调用的函数。
FileOutputStream fos = new FileOutputStream
(preferences.getAppDataLocation() + "history" , true);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject( new Stuff(stuff) );
out.close();
But when I try to read it I only get the first in the file.
Then I get java.io.StreamCorruptedException
.
但是当我尝试阅读它时,我只能得到文件中的第一个。然后我得到java.io.StreamCorruptedException
。
To read I am using
阅读我正在使用
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() );
}
I do not know how many objects will be present so I am reading while there are no exceptions. From what Google says this is not possible. I was wondering if anyone knows a way?
我不知道会有多少对象出现,所以我正在阅读,没有例外。从谷歌所说这是不可能的。我想知道是否有人知道方法?
采纳答案by Andreas Dolk
Here's the trick: subclass ObjectOutputStream
and override the writeStreamHeader
method:
这是诀窍:子类化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();
}
}
To use it, just check whether the history file exists or not and instantiate either this appendable stream (in case the file exists = we append = we don't want a header) or the original stream (in case the file does not exist = we need a header).
要使用它,只需检查历史文件是否存在并实例化这个可追加流(如果文件存在 = 我们追加 = 我们不需要头)或原始流(如果文件不存在 =我们需要一个标题)。
Edit
编辑
I wasn't happy with the first naming of the class. This one's better: it describes the 'what it's for' rather then the 'how it's done'
我对班级的第一个命名并不满意。这个更好:它描述了“它的用途”而不是“它是如何完成的”
Edit
编辑
Changed the name once more, to clarify, that this stream is only for appending to an existing file. It can't be used to create a newfile with object data.
再次更改名称,以澄清此流仅用于附加到现有文件。它不能用于创建带有对象数据的新文件。
Edit
编辑
Added a call to reset()
after this questionshowed that the original version that just overrode writeStreamHeader
to be a no-op could under some conditions create a stream that couldn't be read.
reset()
在此问题显示刚刚覆盖writeStreamHeader
为无操作的原始版本可能在某些条件下创建无法读取的流之后添加了一个调用。
回答by Michael Myers
Because of the precise format of the serialized file, appending will indeed corrupt it. You have to write all objects to the file as part of the same stream, or else it will crash when it reads the stream metadata when it's expecting an object.
由于序列化文件的精确格式,追加确实会破坏它。您必须将所有对象作为同一流的一部分写入文件,否则它会在它期望对象时读取流元数据时崩溃。
You could read the Serialization Specificationfor more details, or (easier) read this threadwhere Roedy Green says basically what I just said.
您可以阅读序列化规范以获取更多详细信息,或者(更容易)阅读此线程,其中 Roedy Green 基本上说了我刚刚说的内容。
回答by Tadeusz Kopec
As the APIsays, the ObjectOutputStream
constructor writes the serialization stream header to the underlying stream. And this header is expected to be only once, in the beginning of the file. So calling
正如API所说,ObjectOutputStream
构造函数将序列化流标头写入底层流。并且这个头文件预计只有一次,在文件的开头。所以打电话
new ObjectOutputStream(fos);
multiple times on the FileOutputStream
that refers to the same file will write the header multiple times and corrupt the file.
多次FileOutputStream
引用同一个文件将多次写入标头并损坏文件。
回答by Michael Borgwardt
The easiest way to avoid this problem is to keep the OutputStream open when you write the data, instead of closing it after each object. Calling reset()
might be advisable to avoid a memory leak.
避免此问题的最简单方法是在写入数据时保持 OutputStream 打开,而不是在每个对象之后关闭它。reset()
可能建议调用以避免内存泄漏。
The alternative would be to read the file as a series of consecutive ObjectInputStreams as well. But this requires you to keep count how many bytes you read (this can be implementd with a FilterInputStream), then close the InputStream, open it again, skip that many bytes and only then wrap it in an ObjectInputStream().
另一种方法是将文件作为一系列连续的 ObjectInputStreams 读取。但这需要您计算读取的字节数(这可以通过 FilterInputStream 实现),然后关闭 InputStream,再次打开它,跳过那么多字节,然后才将其包装在 ObjectInputStream() 中。
回答by user4147874
How about before each time you append an object, read and copying all the current data in the file and then overwrite all together to file.
在每次追加对象之前,读取并复制文件中的所有当前数据,然后将所有数据一起覆盖到文件中。
回答by Pratanu Mandal
I have extended the accepted solution to create a class that can be used for both appending and creating new file.
我扩展了已接受的解决方案以创建一个可用于附加和创建新文件的类。
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);
}
}
}
This class can be used as a direct extended replacement for ObjectOutputStream. We can use the class as follows:
此类可用作 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();
}
}
}