Не удается прочитать object() из внешнего файла при запуске моего JAR

#java #deserialization

#java #десериализация

Вопрос:

У меня есть моя программа в файле 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. Я инициализирую его из метода main, поскольку местоположение не предназначено для изменения. Вот код, который я использовал для его получения, если это уместно:

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

Редактировать: понял это. По-видимому, по какой-либо причине UID ImageIcon из изображения в моем классе шаблонов не будет совпадать при запуске jar с помощью двойного щелчка. Перенос изображения в другой класс с фиксированным идентификатором UID решил проблему.

Комментарии:

1. Почему что происходит? Что IOException ?

2. @MarquisofLorne javax.swing. ImageIcon; локальный класс несовместим.

3. Неадекватно. Опубликуйте все исключение и его трассировку стека в вашем вопросе. Также код, который записал файл. Но вы, похоже, полностью проигнорировали предупреждение в Javadoc для этого класса о его сериализации.

4. @MarquisofLorne Я только что опубликовал трассировку стека и код записи файла.

5. Это именно то, что я сказал. Вы проигнорировали предупреждение и делаете то, что в нем сказано не делать: сериализуете класс Swing с одной версией Java и десериализуете его с другой. Решение: не делайте этого.

Ответ №1:

Конечно, ваш план терпит неудачу: «Тот же каталог, что и мой jar-файл», очевидно, будет содержать ….. ваш jar-файл.

Который приведет к сбою, если вы попытаетесь прочитать его с помощью OIS. Примените некоторую фильтрацию при перемещении по этому каталогу. Имеет ли оно правильное расширение? Это фактический файл (или каталог)?

Кроме того, в вашем коде есть утечка памяти; dirstreams — это ресурсы, как и a FileInputStream . Вы должны закрыть их. Единственный безопасный способ сделать это — попробовать / finally или попробовать с помощью ресурсов. Найдите в Интернете «Java try with resources», если вы не знакомы с этими методами.

Комментарии:

1. Все верно, но попытка десериализации файла JAR приведет к new ObjectInputStream , а не readObject() . Я подозреваю, что это то, что происходит на самом деле.

2. Я не знаю, правильно ли я это понимаю, но я не десериализирую jar, у меня есть родительская папка, содержащая как jar, так и файловую папку. То, что я пытаюсь десериализовать, — это содержимое файловой папки, и оно срабатывает при cmd, если я перехожу в родительскую папку и запускаю jar оттуда.

Ответ №2:

Вы сохранили JDK11 ImageIcon и пытаетесь десериализовать его обратно на виртуальной машине, у которой этого нет, потому что у нее есть JDK14 ImageIcon или что-то еще.

В более общем плане, вы не можете ничего сериализовать, если цель состоит в том, чтобы разрешить его десериализацию в другой системе, если им не очень тщательно управляют. ImageIcon по намерению не управляется таким образом, поэтому вы не можете сохранять ImageIcons с OIS и OOS. Похоже, что вы можете, но есть нюанс: разрывы на разных виртуальных машинах.

Вы должны выбросить весь этот код и подумать о другом способе сериализации объектов ImageIcon. Возможно, сохраните необработанные пиксели, сохраните их в формате PNG и т. Д.