【问题标题】:Java - "invalid stream header" with Serialization of ArrayListJava - 带有 ArrayList 序列化的“无效流标头”
【发布时间】:2014-05-21 15:00:32
【问题描述】:

我的 java 项目有问题。经过几次搜索,我仍然被阻止。

我有一个名为 ListFromFile 的类扩展 ArrayList

public class ListFromFile<T> extends ArrayList<T> implements Serializable {

    private String listFile;

    public ListFromFile() {
        this.listFile = null;
    }

    public ListFromFile(String file) {
        this.listFile = file;
    }

    public void loadDataFromFile() { // Deserialize from file

        try {
            FileInputStream fileInput = new FileInputStream(this.listFile);

            if ( fileInput.read() == -1 ) { // If file is empty
                this.saveDataInToFile();
            } else {
                ObjectInputStream objectInput = new ObjectInputStream(fileInput);

                this.addAll((ArrayList) objectInput.readObject());

                objectInput.close();
            }

            fileInput.close();
        } catch ( IOException | ClassNotFoundException e ) {
            e.printStackTrace();
        }
    }

    public void saveDataInToFile() { // Serialize the ArrayList
        try {
            FileOutputStream fileOutput = new FileOutputStream(this.listFile, true);
            ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput);

            objectOutput.writeObject(this);

            objectOutput.flush();
            objectOutput.close();
            fileOutput.close();
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }

}

在我的主类BZ2MServer 中,我使用 ListFromFile 创建了一些对象:

public class BZ2MServer {

    private static ListFromFile<Specialite> specialites = new ListFromFile<Specialite>("liste_specialites.txt");
    private static ListFromFile<Module> modules = new ListFromFile<Module>("liste_modules.txt");
    private static ListFromFile<Professeur> professeurs = new ListFromFile<Professeur>("liste_professeurs.txt");
    private static ListFromFile<Etudiant> etudiants = new ListFromFile<Etudiant>("liste_etudiants.txt");
    private static ListFromFile<Note> notes = new ListFromFile<Note>("liste_notes.txt");

    public static void main(String[] args) {
        specialites.loadDataFromFile();
    }
}

当应用程序启动时,它会加载保存在文件中的列表。这就是为什么我在 loadDataFromFile() 方法中执行 saveDataInToFile() 的原因。 这样,在我们第一次启动应用程序时,文件应该包含空的 ArrayList。

结果是第一次启动没有问题。但是,在此之后,我总是从编译器那里得到这个:

java.io.StreamCorruptedException: invalid stream header: ED000573
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at bz2m.list.ListFromFile.loadDataFromFile(ListFromFile.java:31)
    at bz2m.server.BZ2MServer.main(BZ2MServer.java:22)

我在BZ2MServer的主要测试了这个:

public static void main(String[] args) {
    Specialite test = new Specialite(1, "Test");

    specialites.add(test2);

    specialites.saveDataInToFile();
}

然后,我删除了之前的代码,使用LoadDataFromFile() 来获取之前的数据。我得到了错误。然后,我在ListFromFile 类中删除了该条件:

if ( fileInput.read() == -1 ) {
    this.saveDataInToFile();
}

它奏效了。但是,在文件为空的情况下,该条件是必要的。没有它,如果文件为空,我会得到这个:

java.io.EOFException
    at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2325)
    at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2794)
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:801)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
    at bz2m.list.ListFromFile.loadDataFromFile(ListFromFile.java:31)
    at bz2m.server.BZ2MServer.main(BZ2MServer.java:22)

我希望我已经为您提供了帮助我所需的所有信息。如果你需要,我可以上传整个项目(不是很大)。

谢谢!

【问题讨论】:

    标签: java arrays list serialization arraylist


    【解决方案1】:

    您使用的序列化方法包括一个magic number verification check,它用于确定您的数据是否有效。因此,如果它与预期不同,它将抛出一个异常,告诉您您的输入不符合其序列化标准。

    在这种情况下,它期望前两个字节为AC ED,后跟版本00 05。你可以在here的常量列表中看到它们。

    invalid stream header: ED000573 表示它接收到的输入的前 4 个字节是ED 00 05 73。如果你在十六进制查看器中打开它,你可能会感到困惑:从位置 0 开始的第一个字节确实是AC ED 00 05,那么问题是什么?

    您的加载方法失败的原因正是因为您进行了检查:

    if ( fileInput.read() == -1 ) {
        this.saveDataInToFile();
    }
    

    这会读取一个字节,这会将文件指针递增到文件中的第二个字节。现在,当您将流传递给ObjectInputStream 进行加载时,它将从当前指针所在的位置读取前4 个字节,参见ED 00 05 73,然后失败。您可以通过查看查看器中的下一个字节来自己验证这一点。

    如果你想检查你的文件是否不为空,而不是读取一个字节来检查是否有字节,直接查询通道的长度即可:

    if (fileInput.getChannel().size() == 0) {
        // empty file
    }
    

    【讨论】:

    • 嗯,非常感谢 MxyL ! fileInput.getChannel().length 不存在,所以我使用了fileInput.getChannel().size(),然后它完美运行:)
    • if ( fileInput.available() == 0 ) 代替if ( fileInput.getChannel().size() == 0 ) 不好吗?
    • 糟糕,是size() 而不是length 我通常会把这两个搞混。 available 在这种情况下应该没问题。我只是不太喜欢描述中的声音。
    猜你喜欢
    • 2016-02-10
    • 1970-01-01
    • 2015-07-25
    • 2015-07-15
    • 1970-01-01
    • 2014-04-24
    • 2011-02-07
    • 2013-06-25
    • 2021-05-31
    相关资源
    最近更新 更多