Почему в моем тесте возникает исключение java.io.IOException: Stream Closed?

Работая над UT проекта Java 11 с Springboot, я нашел следующую трассировку стека:

java.io.IOException: Stream Closed
    at java.base/java.io.FileInputStream.readBytes(Native Method)
    at java.base/java.io.FileInputStream.read(FileInputStream.java:279)
    at com.amazonaws.internal.SdkFilterInputStream.read(SdkFilterInputStream.java:90)
    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107)
    at 

Я попытался создать минимальный воспроизводимый пример:


@Test
    public void minimalExampleTest() throws IOException {
        when(s3Service.getObject(Mockito.anyString())).thenReturn(getS3Object("image.jpg"));

        try(FileOutputStream fos = new FileOutputStream( "src/test/resources/exported_data_test.zip");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            ZipOutputStream zos = new ZipOutputStream(bos)){
            service.minimalExample(zos, "directory/s3");
        }

        ZipFile zipFile = new ZipFile( "src/test/resources/exported_data_test.zip");
        List<String> entries = zipFile.stream().map(ZipEntry::getName).collect(Collectors.toList());

        //assert
        assertNotNull(entries);
        assertTrue(entries.stream().filter(ent -> ent.contains(".jpg")).collect(Collectors.toList()).size() == 4);
        List<String> imagesNameList = List.of("doc1_export.jpg", "doc2_export.jpg", "doc3_export.jpg", "doc4_export.jpg");
        entries.stream().forEach(imageName -> assertTrue(imagesNameList.contains(imageName)));

        //generated files deletion
        entries.stream().forEach(imageName -> {
            File file = new File("src/test/resources" + "/" + imageName);
            file.delete();
        });
        File file = new File( "src/test/resources/exported_data_test.zip");
        file.delete();
    }

    private static S3Object getS3Object(String fileName) throws FileNotFoundException {
        S3Object s3Object = new S3Object();
        s3Object.setKey(fileName);
        File initialFile = new File(IMAGES_DOSSIER + "/" + fileName);
        InputStream targetStream = new FileInputStream(initialFile);
        s3Object.setObjectContent(targetStream);
        return s3Object;
    }
 public ZipOutputStream minimalExample (ZipOutputStream zipOutputStream, String directoryName) {
        try {
            List<String> listDocumentType = List.of("doc1", "doc2", "doc3", "doc4");
            Map<String, S3ObjectInputStream> s3ObjectInputStreamList = new HashMap<>();
            listDocumentType.stream().forEach(docType -> {
                String fileName = directoryName + '/' + docType + "_export.jpg";
                S3Object s3Object = s3Service.getObject(fileName);
                if (s3Object != null)
                    s3ObjectInputStreamList.put(docType + "_export.jpg", s3Object.getObjectContent());
            });

            s3ObjectInputStreamList.keySet().forEach(key -> {
                var zipEntry = new ZipEntry(key);
                try {
                    zipOutputStream.putNextEntry(zipEntry);
                    var objectContent = s3ObjectInputStreamList.get(key);
                    var bytes = new byte[1024];
                    int length;
                    while ((length = objectContent.read(bytes)) >= 0) {
                        zipOutputStream.write(bytes, 0, length);
                    }
                    objectContent.close();
                    zipEntry.clone();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            zipOutputStream.closeEntry();
            zipOutputStream.close();
        } catch (IOException e) {
            logger.log(Level.LOW, e.getMessage());
        }
        return zipOutputStream;
    }

Дело в том, что код при использовании в приложении работает хорошо, но в тесте он делает первую итерацию while и на второй итерации выскакивает ошибка, показанная в трассировке стека. (В минимальном примере тест пройден - ошибка не блокируется)

Я не знаю, почему у меня возникает ошибка java.io.IOException: Stream Closed. Если кто-нибудь может дать объяснение/решение этой проблемы?

Вы закрываете поток где-то между вторым и первым фрагментом кода. минимальный воспроизводимый пример, пожалуйста

Sören 11.07.2024 17:29

Возможно, у вас есть два ключа с тем же входным потоком, что и значение. Поэтому, когда вы закрываете поток ввода первого ключа и пытаетесь прочитать поток ввода второго ключа, он уже закрыт.

k314159 11.07.2024 17:31

закрыть блокfinally{}

Ali.Mojtahed 11.07.2024 22:52

@Sören Я попытался создать пример (см. изменения в этом посте).

paymer 17.07.2024 09:20

@ k314159 k314159 Я проверил ключи, и они все разные.

paymer 17.07.2024 09:20
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
5
64
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема заключалась в том, что мы вызывали метод в S3Service 4 раза и возвращали одно и то же изображение, поэтому, когда мы пытались прочитать содержимое объекта, поток уже был создан.

Проблема была обнаружена с указанием возврата макета в разные файлы, как в приведенном ниже коде:

@Test
    public void minimalExampleTest() throws IOException {
        when(s3Service.getObject(Mockito.anyString())).thenReturn(getS3Object("image.jpg"))
.thenReturn(getS3Object("image1.jpg"))
.thenReturn(getS3Object("image2.jpg"))
.thenReturn(getS3Object("image3.jpg"));

        try(FileOutputStream fos = new FileOutputStream( "src/test/resources/exported_data_test.zip");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            ZipOutputStream zos = new ZipOutputStream(bos)){
            service.minimalExample(zos, "directory/s3");
        }

        ZipFile zipFile = new ZipFile( "src/test/resources/exported_data_test.zip");
        List<String> entries = zipFile.stream().map(ZipEntry::getName).collect(Collectors.toList());

        //assert
        assertNotNull(entries);
        assertTrue(entries.stream().filter(ent -> ent.contains(".jpg")).collect(Collectors.toList()).size() == 4);
        List<String> imagesNameList = List.of("doc1_export.jpg", "doc2_export.jpg", "doc3_export.jpg", "doc4_export.jpg");
        entries.stream().forEach(imageName -> assertTrue(imagesNameList.contains(imageName)));

        //generated files deletion
        entries.stream().forEach(imageName -> {
            File file = new File("src/test/resources" + "/" + imageName);
            file.delete();
        });
        File file = new File( "src/test/resources/exported_data_test.zip");
        file.delete();
    }

Другие вопросы по теме