Question

est-il pas possible d'ajouter à un ObjectOutputStream?

Je suis en train d'ajouter à une liste d'objets. extrait suivant est une fonction qui est appelée chaque fois qu'un travail est terminé.

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

out.writeObject( new Stuff(stuff) );
out.close();

Mais quand j'essaie de le lire, je reçois le premier que dans le fichier. Puis-je obtenir java.io.StreamCorruptedException.

Pour lire, je me sers

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

Je ne sais pas combien d'objets seront présents, donc je suis en train de lire alors qu'il n'y a aucune exception. D'après ce que Google dit que ce n'est pas possible. Je me demandais si quelqu'un sait d'une manière?

Était-ce utile?

La solution

Voici l'astuce: la sous-classe ObjectOutputStream et passer outre la méthode 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();
  }

}

Pour l'utiliser, il suffit de vérifier si le fichier historique existe ou non et instancier soit ce flux réinscriptible (dans le cas où le fichier existe = nous append = nous ne voulons pas un en-tête) ou le courant d'origine (dans le cas où le fichier ne existions pas = nous avons besoin d'un en-tête).

Modifier

Je n'étais pas satisfait de la première nomination de la classe. Celui-ci est mieux: il décrit la « ce qu'il est pour » plutôt que le « comment faire »

Modifier

a changé le nom une fois de plus, de préciser, que ce flux est uniquement pour annexant à un fichier existant. Il ne peut pas être utilisé pour créer un nouveau fichier avec des données d'objet.

Modifier

Ajout d'un appel à reset() après cette question a montré que la version originale qui vient d'être passé outre writeStreamHeader un no-op pourrait dans certaines conditions créent un flux qui ne pouvait pas être lu.

Autres conseils

API dit, le constructeur ObjectOutputStream écrit l'en-tête de flux de sérialisation au courant sous-jacent. Et cet en-tête devrait être qu'une seule fois, au début du fichier. Donc, appeler

new ObjectOutputStream(fos);

plusieurs fois sur la FileOutputStream qui fait référence au même fichier écriront l'en-tête plusieurs fois et le fichier corrompu.

En raison du format précis du fichier sérialisé, annexant sera en effet corrompre. Vous devez écrire tous les objets du fichier dans le cadre du même flux, ou bien il se bloque quand il lit les métadonnées de flux quand il attend un objet.

Vous pouvez lire sérialisation Spécification pour plus de détails, ou (plus facile) lire ce fil où Roedy vert dit essentiellement ce que je viens de dire.

La meilleure façon d'éviter ce problème est de garder le OutputStream ouvert lorsque vous écrivez les données, au lieu de fermer après chaque objet. L'appel reset() pourrait être souhaitable d'éviter une fuite de mémoire.

L'alternative serait de lire le fichier comme une série de ObjectInputStreams consécutifs ainsi. Mais cela vous oblige à tenir le compte combien d'octets vous avez lu (ce qui peut être implementd avec un FilterInputStream), puis fermez le InputStream, ouvrez-le à nouveau, sauter que beaucoup d'octets et ne puis l'envelopper dans un ObjectInputStream ().

Que diriez-vous avant chaque fois que vous ajoutez un objet, lire et copier toutes les données actuelles dans le fichier, puis écraser tous ensemble un fichier.

Je l'ai étendu la solution retenue pour créer une classe qui peut être utilisé aussi bien pour appending et la création de nouveau fichier.

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

}

Cette classe peut être utilisée comme remplacement prolongeaient directe pour ObjectOutputStream. Nous pouvons utiliser la classe comme suit:

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

    }

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top