无法在运行我的JAR文件时从外部文件中读取readObject()。

huangapple go评论70阅读模式
英文:

Can't readObject() from an external file when running my JAR

问题

我有一个程序存储在一个JAR文件中,还有一些文件存储在同一目录的文件夹中。经过几个小时的搜索,我找到了一些代码,可以让我列出该文件夹中的文件(因为 File.listFiles() 不起作用),但现在 readObject() 给我抛出了 IOException 异常。当我从IDE或cmd运行它时,它运行正常。只有在双击运行JAR文件时才会抛出异常。

以下是我正在做的事情:

public static Template[] loadTemplates() throws IOException, ClassNotFoundException {
        Path dirtemp = Paths.get(path.toString(), TEMPLATE_PATH);
        DirectoryStream<Path> dirStream = Files.newDirectoryStream(dirtemp);
        
        ArrayList<File> files = new ArrayList<>();

        for (Path entry: dirStream) {
            files.add(entry.toFile());
        }

        if (files.size() != 0) {
            ArrayList<Template> templates = new ArrayList<>();

            for (File file: files) {
                FileInputStream fis = new FileInputStream(file);
                ObjectInputStream ois = new ObjectInputStream(fis);

                Object object = ois.readObject();

                if (object instanceof Template) {
                    templates.add((Template) object);
                }
                
                ois.close();
                fis.close();
            }

            Collections.sort(templates);

            return templates.toArray(new Template[0]);
        } else return null;
    }

TEMPLATE_PATH 只是一个包含文件夹名称的字符串。

path 是我的类中的一个静态变量,包含JAR文件的当前路径。我从主方法中初始化它,因为位置不打算更改。以下是我用于获取它的代码,如果相关的话:

public static void findJARPath() {
        final Class<?> referenceClass = Main.class;
        final URL url =
                referenceClass.getProtectionDomain().getCodeSource().getLocation();

        try {
            final File jarPath = new File(url.toURI()).getParentFile();
            path = Paths.get(jarPath.toURI());
        } catch(final URISyntaxException ignored){}
    }

有人知道为什么会发生这种情况吗?

我在Windows上运行,但我希望它也能在不同的操作系统上运行。如果您指出这些代码中有哪些部分不具有可移植性,我将不胜感激。

编辑:异常的堆栈跟踪:

javax.swing.ImageIcon local class incompatible: stream class desc serialVersionUID = -962022720109015502, local class serialVersionUID = 532615968316031794
java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1903)
java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1772)
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2355)
java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2249)
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087)
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
Data.FileManager.loadTemplates(FileManager.java:108)

写入文件的代码:

public static void saveTemplate(Template template) {
        new File(TEMPLATE_PATH).mkdir();
        String filename = StringUtils.toFilename(template.getName(), "templ");

        try {
            FileOutputStream fos = new FileOutputStream(TEMPLATE_PATH + filename);
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(template);

            oos.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

编辑:找到解决方法了。显然,由于某种原因,我的模板类中的图像的ImageIcon UID在通过双击运行JAR文件时不匹配。将图像包装成具有固定UID的另一个类解决了问题。

英文:

I have my program in a JAR file, and some files in a folder in the same directory. After hours of searching I found some code that allowed me to list the files inside that folder (since File.listFiles() didn't work), but now readObject() gives me IOException. It works fine when I run it from the IDE or with cmd. It only throws the exception when running the JAR via double click.

Here's what I'm doing:

public static Template[] loadTemplates() throws IOException, ClassNotFoundException {
        Path dirtemp = Paths.get(path.toString(), TEMPLATE_PATH);
        DirectoryStream<Path> dirStream = Files.newDirectoryStream(dirtemp);
        
        ArrayList<File> files = new ArrayList<>();

        for (Path entry: dirStream) {
            files.add(entry.toFile());
        }

        if (files.size() != 0) {
            ArrayList<Template> templates = new ArrayList<>();

            for (File file: files) {
                FileInputStream fis = new FileInputStream(file);
                ObjectInputStream ois = new ObjectInputStream(fis);

                Object object = ois.readObject();

                if (object instanceof Template) {
                    templates.add((Template) object);
                }
                
                ois.close();
                fis.close();
            }

            Collections.sort(templates);

            return templates.toArray(new Template[0]);
        } else return null;
    }

TEMPLATE_PATH is just a string that contains the name of the folder with the files.

path is a static variable inside my class that contains the current path to the JAR. I initialize it from the main method, since the location is not intended to change. Here's the code that I used to get it, if relevant:

public static void findJARPath() {
        final Class<?> referenceClass = Main.class;
        final URL url =
                referenceClass.getProtectionDomain().getCodeSource().getLocation();

        try {
            final File jarPath = new File(url.toURI()).getParentFile();
            path = Paths.get(jarPath.toURI());
        } catch(final URISyntaxException ignored){}
    }

Does anyone know why this happens?

I'm running on Windows but I want this to run on different OS as well. I'd appreciate if you pointed out if any of this code is not portable, too.

Edit:
The stack trace of the exception:

javax.swing.ImageIcon local class incompatible: stream class desc serialVersionUID = -962022720109015502, local class serialVersionUID = 532615968316031794
java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1903)
java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1772)
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2355)
java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2249)
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087)
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
Data.FileManager.loadTemplates(FileManager.java:108)

The code that writes the files:

public static void saveTemplate(Template template) {
        new File(TEMPLATE_PATH).mkdir();
        String filename = StringUtils.toFilename(template.getName(), "templ");

        try {
            FileOutputStream fos = new FileOutputStream(TEMPLATE_PATH + filename);
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(template);

            oos.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Edit: Figured it out. Apparently, for whatever reason, the ImageIcon UID from the image in my template class wouldn't match when running the jar via double click. Wrapping the image into another class with a fixed UID solved it.

答案1

得分: 2

你的计划自然会失败:当然,“与我的jar文件相同的目录”显然会包含..... 你的jar文件。

如果你尝试使用OIS来读取它,程序将崩溃。在遍历该目录时,通过一些过滤器来进行筛选。它是否具有正确的扩展名?它是否是一个实际的文件(而不是一个目录)?

另外,你的代码存在内存泄漏;dirstreams(目录流)是资源,FileInputStream 也是。你必须关闭它们。唯一安全的方法是使用try/finally或者try-with-resources。如果你不熟悉这些技术,请在网上搜索“Java try with resources”。

英文:

Your plan defeats itself, of course: "The same directory as my jar file" would obviously contain..... your jar file.

Which will crash if you attempt to read it with an OIS. Apply some filtering as you dirstream through this directory. Does it have the right extension? Is it an actual file (vs. a directory)?

Also, your code has a memory leak; dirstreams are resources, as is a FileInputStream. You must close these. The only safe way to do so is with try/finally or try-with-resources. Search the web for "Java try with resources" if you're unfamiliar with these techniques.

答案2

得分: 0

你已经保存了一个JDK11的ImageIcon,并且正试图在一个没有它的VM上对其进行反序列化,因为它有一个JDK14的ImageIcon或其他类似的内容。

更一般地说,如果意图是在另一个系统上允许对其进行反序列化,那么除非进行了非常谨慎的管理,否则无法对任何内容进行序列化。ImageIcon的设计并不是这样管理的,因此不能使用OIS和OOS保存ImageIcon。看起来你是可以的,但是要注意的是:在不同的VM上可能会出现问题。

你需要放弃所有这些代码,并考虑另一种方法来序列化ImageIcon对象。也许可以保存原始像素,将其保存为PNG格式等。

英文:

You've saved a JDK11 ImageIcon, and are trying to deserialize it back on a VM that doesn't have that, because it has a JDK14 ImageIcon or whatnot.

More generally, you can't serialize anything if the intent is to allow it to deserialize on another system unless it is very carefully managed. ImageIcon by intent is not managed like that, therefore, you can't save ImageIcons with OIS and OOS. It looks like you can, but the caveat is: Breaks on different VMs.

You gotta toss all this code out and think of another way to serialize ImageIcon objects. Perhaps save the raw pixels, save it as a PNG, etc.

huangapple
  • 本文由 发表于 2020年10月26日 06:48:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/64529603.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定