ObjectInputStream доволен FileInputStream, не доволен getResourceAsStream

У меня есть довольно стандартный код, который берет сериализованный объект из потока, который в основном выглядит следующим образом:

  Object getObjectFromStream(InputStream is) {
    ObjectInputStream ois = new ObjectInputStream(is);
    return ois.readObject();
  }

Затем у меня есть файл в папке моих ресурсов, поэтому на моей машине разработки я могу либо ссылаться на него как на файл, либо как на JarResource:

  InputStream is = new FileInputStream("/home/.../src/main/resources/serializedObjects/testObject");
  InputStream is = this.getClass().getResourceAsStream("/serializedObjects/testObject");

В моей голове оба должны делать то же самое. Однако, как это происходит, оба разрешают действительный (не нулевой) поток, но FileInputStream корректно возвращает объект из моего метода getObjectFromStream (InputStream), в то время как версия getResourceAsStream выдает это исключение:

  java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:800)
    at java.io.ObjectInputStream.(ObjectInputStream.java:297)

В основном, я хотел бы знать, как это исправить, но я также хотел бы узнать разницу между двумя InputStreams...

Ответ 1

EFBFBD - это представление UTF-8 символа замены Unicode U + FFFD. Таким образом, похоже, что файл прошел через процесс преобразования кодировки.

Maven может быть подозреваемым, особенно его фильтрацией ресурсов.

Ответ 2

В вашем случае это был Maven, который возился с вашими файлами, однако я нашел то же самое по другой причине и поэтому документирую его здесь, поскольку это единственный полезный результат поиска в Google.

Я сохранил сериализованные объекты в качестве наборов данных для модульных тестов и сохранил их в управлении версиями. Было ли это хорошей идеей или нет, для обсуждения, но в другой раз.

Файлы начинаются с:

AC ED 00 05 ...

После сохранения их в Git они становятся:

EF BF BD EF BF BD 00 05 ...

Это вызывает ошибку:

java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:782)
    at java.io.ObjectInputStream.<init>(ObjectInputStream.java:279)

Git не только изменяет эти начальные байты, но и много байтов по всему файлу. Он пытается конвертировать между окончанием строки стиля Windows и Unix. Эвристика используется для определения того, не содержит ли файл текст.

Решением было добавить файл .gitattributes, в котором некоторые файлы были исключены из этой обработки:

*.bytes -crlf

Я также обеспечил, чтобы мой файл .git/config имел следующее:

[core]
    autocrlf = false

С этими изменениями я удалил индекс и заставил reset:

rm .git/index
git reset      # force rescan of the index
git status     # any files listed here will experience changes
git add -u
git commit -m "Line ending normalisation changes."

Надеюсь, что кто-то поможет. Я не гуру Git, поэтому может быть, что некоторые из этих шагов не нужны, но они работали для меня.

Ответ 3

Это работало для меня.

        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>2.5</version>
          <configuration>
            <encoding>UTF-8</encoding>
            <nonFilteredFileExtensions>
              <nonFilteredFileExtension>xls</nonFilteredFileExtension>
              <nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
              <nonFilteredFileExtension>jrxml</nonFilteredFileExtension>
              <nonFilteredFileExtension>jasper</nonFilteredFileExtension>
            </nonFilteredFileExtensions>
          </configuration>
        </plugin>

Ответ 4

Одна проблема заключается в том, что maven пытается отфильтровать все в папке ресурса. Создайте отдельную папку и затем инструктируйте maven не фильтровать ее.

<resources>
   <resource>
       <directory>${basedir}/bin</directory>
       <filtering>false</filtering>
       <includes>
           <include>**/*</include>
       </includes>
   </resource>
</resources>

Ответ 5

Не должно быть никакой разницы - путь, который вы используете для getResourceAsStream(), должен найти другой файл. Выполните поиск других файлов, сохраненных как serializedObjects/testObject, и посмотрите, не можете ли вы их найти. Помните, что FileInputStream будет выглядеть относительно текущего каталога, в то время как getResourceAsStream() относится к пути к классу.