10

java.io.StreamCorruptedExceptionのところ、オブジェクトを追加しようとすると取得します。私はそれを克服する方法をインターネットで検索しました。私がこれまでに見つけた答えは、それができないということです。この問題を回避する方法は、オブジェクトをリストに書き込んでから、リストをファイルに書き込むことです。

ただし、新しいオブジェクトを追加するたびに、そのファイルを上書きする必要があります。残業では最適な解決策ではないようです。

既存のオブジェクトストリームにオブジェクトを追加する方法はありますか?

4

4 に答える 4

5

実際、それは非常に簡単です。writeStreamHeader既存のストリームに追加する場合は、ファイルの途中に2番目のヘッダーが書き込まれないようにオーバーライドするObjectOutStreamのサブクラスを使用する必要があります。例えば

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

次に、標準のObjectInputStreamを使用してファイル全体を読み取ります。

于 2010-01-19T16:44:36.260 に答える
5

このトピックで私が見つけた最高の記事は次のとおりです。http: //codify.flansite.com/2009/11/java-serialization-appending-objects-to-an-existing-file/

ObjectOutputStreamをオーバーライドする「ソリューション」は単に間違っています。それによって引き起こされたバグの調査を終えたところです(貴重な2日間を無駄にしました)。シリアル化されたファイルが破損することがあるだけでなく、例外をスローせずに読み取り、最終的にガベージデータ(混合フィールド)を提供することさえできました。信じられない人のために、私は問題を明らかにするいくつかのコードを添付しています:

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

その記事で述べられているように、次のいずれかの解決策を使用できます。

解決策#1:単一のストリームで複数のファイルを偽造する

..。

「トランザクション」をByteArrayOutputStreamに書き込み、次にこのByteArrayOutputStreamの長さと内容をDataOutputStreamを介してファイルに書き込みます。

解決策2:再度開いてスキップする

別の解決策は、以下を使用してファイル位置を保存することです。

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

次のトランザクションを読み取る前に、ファイルを閉じてファイルを再度開き、この位置にスキップします。

于 2012-01-13T18:15:03.000 に答える
3

コードの公開に問題を抱えてくれたGeorgeHateganに感謝します。私もしばらく調べました。それから、それは私を襲った。writeStreamHeader()メソッドをオーバーライドしてサブクラス化されたObjectOutputStreamを使用してデータを書き込む場合は、readStreamHeader( メソッドをオーバーライドして並列サブクラス化されたObjectInputStreamを使用してデータを読み取る必要があります。もちろん、オブジェクトの書き込みと読み取りのさまざまな実装間でジグザグに移動することはできますが、書き込み/読み取りプロセスで対応するサブクラスのペアを使用する限り、(うまくいけば)問題ありません。トム。

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
        }
    }
}
于 2012-03-15T23:38:31.327 に答える
0

ObjectInputStreamすべてに一致する新しいを作成する必要がありますObjectOutputStreamObjectInputStream状態を完全から完全に転送する方法がわかりませんObjectOutputStream(完全な再実装なしで、とにかく純粋なJavaでは少し注意が必要です)。

于 2010-01-19T16:02:18.530 に答える