Frage

Ist es nicht möglich, eine ObjectOutputStream anhängen?

Ich versuche, eine Liste von Objekten anzuhängen. Folgender Ausschnitt ist eine Funktion, die aufgerufen wird, wenn ein Auftrag abgeschlossen ist.

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

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

Aber wenn ich versuche, es zu lesen bekomme ich nur die erste in der Datei. Dann bekomme ich java.io.StreamCorruptedException.

Zum Lesen Ich bin mit

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

Ich weiß nicht, wie viele Objekte vorhanden sein werden, so lese ich bin während es keine Ausnahmen geben. Von dem, was Google sagt, dass dies nicht möglich ist. Ich frage mich, ob jemand eine Möglichkeit kennt?

War es hilfreich?

Lösung

Hier ist der Trick: Unterklasse ObjectOutputStream und überschreibt die writeStreamHeader Methode:

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

}

Um es zu benutzen, überprüfen gerade, ob die Protokolldatei vorhanden ist oder nicht, und instantiate entweder diese appendable Strom (falls die Datei existiert = wir append = wir wollen nicht einen Header) oder den ursprünglichen Strom (falls die Datei wird nicht vorhanden = brauchen wir einen Header).

Bearbeiten

war ich nicht zufrieden mit der ersten Nennung der Klasse. Dieser ist besser: es beschreibt das ‚was es für‘ eher dann das ‚wie es gemacht wird‘

Bearbeiten

änderte den Namen noch einmal, zu klären, dass dieser Strom nur in eine vorhandene Datei zum Anhängen. Es kann nicht verwendet werden, um zu erstellen neue Datei mit Objektdaten.

Bearbeiten

einen Aufruf an reset() Hinzugefügt nach dieser Frage zeigt , dass die Original-Version, dass nur overrode writeStreamHeader einen no-op sein könnte unter bestimmten Bedingungen zu schaffen, um einen Strom, der nicht gelesen werden kann.

Andere Tipps

Als API sagt, schreibt der ObjectOutputStream Konstruktor die Serialisierung Stream-Header an den zugrunde liegenden Stream. Und dieser Header wird erwartet, dass nur ein einziges Mal sein, am Anfang der Datei. So rufen

new ObjectOutputStream(fos);

mehrere Male auf der FileOutputStream, die auf die gleiche Datei bezieht, wird die Kopfzeile mehrere Male und korrupt die Datei schreiben.

Durch die genaue Format der serialisierten Datei angehängt wird es in der Tat beschädigt. Sie müssen alle Objekte in der Datei als Teil des gleichen Strom schreiben, sonst wird es abstürzen, wenn es den Stream Metadaten liest, wenn es ein Objekt erwartet.

Sie könnten lesen Sie die Serialisierung Spezifikation um weitere Informationen, oder (einfacher) lesen dieses Thema wo Roedy Grün sagt im Grunde, was ich gerade gesagt habe.

Der einfachste Weg, um dieses Problem zu vermeiden, ist den Output offen zu halten, wenn Sie die Daten schreiben, anstatt sie nach jedem Objekt zu schließen. Der Aufruf reset() könnte sinnvoll sein, einen Speicherverlust zu vermeiden.

Die Alternative wäre die Datei als eine Reihe von aufeinanderfolgenden ObjectInputStreams als auch zu lesen. Aber dies erfordert, dass Sie immer zählen, wie viele Bytes Sie lesen (dies mit einem Filterinput sein implementd kann), dann schließen die INPUTSTREAM- es wieder öffnen, überspringen, dass viele Bytes und nur dann wickeln Sie es in einem Object ().

Wie wäre es vor jedem Mal, wenn Sie ein Objekt anhängen, lesen und kopieren Sie alle aktuellen Daten in der Datei und dann alle zusammen Datei überschreiben.

Ich habe die akzeptierte Lösung erweitert, um eine Klasse zu erstellen, die sowohl für Anfügen und die Schaffung neue Datei verwendet werden kann.

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

}

Diese Klasse kann als direkter Ersatz für erweiterte Object verwendet werden. Wir können die Klasse wie folgt verwenden:

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

    }

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top