2010-01-19 23 views
10

Por el momento obtendré java.io.StreamCorruptedException cuando intento añadir un Objeto. He buscado en Internet una forma de superar eso. La respuesta que encontré hasta ahora es que no se puede hacer. Una forma de evitar este problema es escribir los objetos en una lista y luego escribir la lista en el archivo.¿Cómo puedo agregar un java.io.ObjectStream existente?

Pero tengo que sobrescribir ese archivo cada vez que agrego nuevos objetos. Parece que no es la solución óptima en tiempo extra.

¿Hay alguna manera de agregar objetos a una secuencia de objetos existente?

+3

¿Tiene una pregunta? – danben

+0

¿Es posible que use una base de datos para persistencia? – Asaph

+0

@danben: sí, la pregunta es si los objetos se pueden agregar a un archivo. @Asaph: Podría hacerlo, pero quiero saber si append no es para objetos. Sry si mi pregunta no aclara eso. – starcorn

Respuesta

5

En realidad, es bastante fácil de hacer. Cuando agrega a una secuencia existente, necesita usar una subclase de ObjectOutStream que sobrescribe writeStreamHeader para que no se escriba un segundo encabezado en el medio del archivo. Por ejemplo

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

Luego use un ObjectInputStream estándar para leer todo el archivo.

+0

Muy agradable, la escritura vacía (byte [] b, int off, int len, boolean copy) utiliza desafortunadamente el método privado writeBlockHeader. – stacker

+0

bueno, funciona :) – starcorn

+0

+1 Lo leí en la documentación, pero (como a menudo me sucede con la documentación) no lo entendí, hasta ahora. – OscarRyz

0

Debería crear una nueva ObjectInputStream para que coincida con cada ObjectOutputStream. No sé cómo transferir el estado de un ObjectInputStream completo a un ObjectOutputStream (sin una reimplementación completa, que de todos modos es un poco complicado en Java puro).

5

El mejor artículo que he encontrado sobre este tema es: http://codify.flansite.com/2009/11/java-serialization-appending-objects-to-an-existing-file/

La "solución" que anula ObjectOutputStream es simplemente errónea. Acabo de terminar de investigar un error que fue causado por eso (perdiendo dos días preciosos). No solo eso a veces corrompería el archivo serializado, sino que incluso podría leer sin lanzar excepciones y al final proporcionar datos basura (campos de mezcla). Para aquellos con incredulidad, Estoy adjuntando un código que expone el problema:

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<String, String> o) throws IOException { 
     ObjectOutputStream oos = getOOS(storageFile); 
     oos.writeObject(o); 
     oos.close(); 
    } 

    private static void read(ObjectInputStream ois, Map<String, String> 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<String, String> getO1() { 
     Map<String, String> nvps = new HashMap<String, String>(); 
     nvps.put("timestamp", "1326382770000"); 
     nvps.put("length", "246"); 
     return nvps; 
    } 

    private static Map<String, String> getO2() { 
     Map<String, String> nvps = new HashMap<String, String>(); 
     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 
     } 
    } 
} 

Como se señala en ese artículo, puede utilizar una de las siguientes soluciones:

Solución # 1: Falso múltiples archivos en un flujo único

...

escribir “transacción” a un ByteArrayOutputStream, a continuación, escribir el longitud y contenido de este ByteArrayOutputStream en un archivo a través del DataOutputStream.

Solución # 2: Vuelva a abrir y Skip

Otra solución consiste en guardar la posición de archivos usando:

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

cerrar el archivo, volver a abrir el archivo, y saltar a esta posición antes de leer el próxima transacción.

+0

Aquí hay un par de funciones (en Scala) que implementan la Solución n. ° 1 –

2

Muchas gracias a George Hategan por el código de exposición del problema. Lo examiné por un tiempo también. Entonces, me golpeó.Si estás utilizando un método sub-clasificarse ObjectOutputStream con una sustitución de la writeStreamHeader() para escribir datos, debe utilizar el paralelo sub-clasificarse ObjectInputStream con una sustitución de la readStreamHeader() método para leer los datos. Por supuesto, podemos zigzaguear entre diferentes implementaciones de escritura y lectura de objetos, pero siempre que usemos los pares correspondientes de subclases en el proceso de escritura/lectura, estaremos (afortunadamente) bien. Tom.

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<String, String> o) 
        throws IOException { 
     ObjectOutputStream oos = getOOS(storageFile); 
     oos.writeObject(o); 
     oos.flush(); 
     oos.close(); 
    } 

    private static void read(FileInputStream fis, Map<String, String> 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<String, String> getO1() { 
     Map<String, String> nvps = new HashMap<String, String>(); 
     nvps.put("timestamp", "1326382770000"); 
     nvps.put("length", "246"); 
     return nvps; 
    } 

    private static Map<String, String> getO2() { 
     Map<String, String> nvps = new HashMap<String, String>(); 
     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 
     } 
    } 
} 
Cuestiones relacionadas