2010-01-19 4 views
10

Pour l'instant, je vais obtenir java.io.StreamCorruptedException lorsque j'essaie d'ajouter un objet. J'ai cherché sur Internet un moyen de surmonter cela. La réponse que j'ai trouvée jusqu'ici est que cela ne peut pas être fait. Un moyen de contourner ce problème est d'écrire les objets dans une liste, puis d'écrire la liste dans le fichier.Comment puis-je ajouter à un java.io.ObjectStream existant?

Mais je dois réécrire ce fichier chaque fois que j'ajoute de nouveaux objets. Cela ne semble pas être la solution optimale en heures supplémentaires.

Existe-t-il un moyen d'ajouter des objets à un flux d'objet existant?

+3

Avez-vous une question? – danben

+0

Est-il possible pour vous d'utiliser une base de données pour la persistance? – Asaph

+0

@danben: oui, la question est de savoir si les objets peuvent être ajoutés à un fichier. @Asaph: Je pourrais le faire, mais je veux savoir si append est non pour les objets. Sry si ma question n'a pas clarifié cela. – starcorn

Répondre

5

C'est en fait assez facile à faire. Lorsque vous ajoutez à un flux existant, vous devez utiliser une sous-classe de ObjectOutStream qui remplace writeStreamHeader afin qu'un second en-tête ne soit pas écrit au milieu du fichier. Par exemple

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

Ensuite, utilisez simplement un ObjectInputStream standard pour lire le fichier entier.

+0

Très agréable, l'écriture vide (byte [] b, int off, int len, copie booléenne) utilise malheureusement la méthode private writeBlockHeader. – stacker

+0

sympa, ça marche :) – starcorn

+0

+1 J'ai lu ça dans la documentation mais (comme cela m'arrive souvent avec la documentation) je ne l'ai pas bien compris, jusqu'à maintenant. – OscarRyz

0

Vous devez créer un nouveau ObjectInputStream correspondant à chaque ObjectOutputStream. Je ne connais pas un moyen de transférer l'état d'un ObjectInputStream complet à un ObjectOutputStream (sans une réimplémentation complète, ce qui est un peu difficile en Java pur de toute façon).

5

Le meilleur article que j'ai trouvé sur ce sujet est: http://codify.flansite.com/2009/11/java-serialization-appending-objects-to-an-existing-file/

La « solution » qui remplace ObjectOutputStream est tout simplement faux. Je viens de finir d'enquêter sur un bug qui a été causé par cela (gaspillant deux jours précieux). Non seulement cela corromprait parfois le fichier sérialisé, mais même réussi à lire sans jeter des exceptions et à la fin fournir des données de poubelle (champs de mélange). Pour ceux dans l'incrédulité, j'attacher un code qui expose le problème:

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 
     } 
    } 
} 

Comme il est indiqué dans cet article, vous pouvez utiliser l'une des solutions suivantes:

Solution # 1: Faux plusieurs fichiers dans un seul flux

...

écrivez votre « transaction » à un ByteArrayOutputStream, puis écrire le longueur et le contenu de ce ByteArrayOutputStream à un fichier via le DataOutputStream.

Solution # 2: Ouvrez de nouveau et Passer

Une autre solution consiste à sauver la position de fichier en utilisant:

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

fermeture du dossier, la réouverture du fichier, et sauter à cette position avant de lire la prochaine transaction.

+0

Voici une paire de fonctions (en Scala) qui implémentent la Solution # 1 –

2

Un grand merci à George Hategan pour le problème exposant le code. Je l'ai examiné pendant un moment aussi. Ensuite, ça m'a frappé.Si vous utilisez un sous-classé ObjectOutputStream avec un remplacement de la writeStreamHeader() méthode pour écrire des données, vous devez utiliser le sous-classé ObjectInputStream parallèle avec un remplacement de la readStreamHeader() méthode pour lire les données. Bien sûr, nous pouvons zigzaguer entre différentes implémentations d'écriture et de lecture d'objets, mais tant que nous utilisons les paires de sous-classes correspondantes dans le processus d'écriture/lecture, nous serons (espérons-le) bien. À M.

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 
     } 
    } 
} 
Questions connexes