java serialization append objectoutputstream objectinputstream

java - Anexando a un ObjectOutputStream



serialization append (5)

¿No es posible agregar un ObjectOutputStream ?

Estoy intentando agregar a una lista de objetos. El siguiente fragmento es una función que se invoca cada vez que se finaliza un trabajo.

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

Pero cuando trato de leerlo solo obtengo el primero en el archivo. Luego recibo java.io.StreamCorruptedException .

Para leer estoy usando

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() ); }

No sé cuántos objetos estarán presentes, así que estoy leyendo mientras no haya excepciones. Por lo que Google dice, esto no es posible. Me preguntaba si alguien sabe una forma de hacerlo.


Como dice la API , el constructor ObjectOutputStream escribe el encabezado de la secuencia de serialización en la secuencia subyacente. Y se espera que este encabezado sea solo una vez, al comienzo del archivo. Entonces llamando

new ObjectOutputStream(fos);

varias veces en el FileOutputStream que se refiere al mismo archivo escribirá el encabezado varias veces y corromperá el archivo.


Debido al formato preciso del archivo serializado, anexar lo corromperá. Debe escribir todos los objetos en el archivo como parte de la misma secuencia o, de lo contrario, se bloqueará cuando lea los metadatos de la secuencia cuando esté esperando un objeto.

Puede leer la Especificación de serialización para obtener más detalles, o (más fácil) leer este hilo donde Roedy Green dice básicamente lo que acabo de decir.


Este es el truco: la subclase ObjectOutputStream y anula el método 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(); } }

Para usarlo, simplemente verifique si el archivo de historial existe o no e instale esta secuencia apta (en caso de que el archivo exista = anexamos = no queremos un encabezado) o la secuencia original (en caso de que el archivo no exista = necesitamos un encabezado).

Editar

No estaba contento con el primer nombramiento de la clase. Este es mejor: describe el "para qué sirve" en lugar de "cómo se hace"

Editar

Se cambió el nombre una vez más, para aclarar, que esta secuencia solo sirve para anexar un archivo existente. No se puede usar para crear un nuevo archivo con datos de objeto.

Editar

Se agregó una llamada para reset() después de que esta pregunta mostrara que la versión original que simplemente anuló writeStreamHeader para ser no writeStreamHeader podría, bajo ciertas condiciones, crear una secuencia que no podría leerse.


La forma más fácil de evitar este problema es mantener abierto el OutputStream cuando se escriben los datos, en lugar de cerrarlo después de cada objeto. Llamar a reset() puede ser aconsejable para evitar una pérdida de memoria.

La alternativa sería leer el archivo como una serie de ObjectInputStreams consecutivos también. Pero esto requiere que cuentes cuántos bytes has leído (esto se puede implementar con un FilterInputStream), luego cierras el InputStream, lo vuelves a abrir, saltas muchos bytes y luego lo envuelves en un ObjectInputStream ().


Qué tal antes de cada vez que anexa un objeto, lea y copie todos los datos actuales en el archivo y luego sobrescriba todos juntos en el archivo.