Как дождаться завершения записи в ZipOutputStream?

Вопрос или проблема

У меня есть Java-приложение, которое извлекает записи из таблицы базы данных, содержащей столбец VARBINARY, в котором хранятся данные файла.

После извлечения записей приложение создает ZipEntry для каждой записи, добавляет его в ZipOutputStream, а затем записывает все это в zip-файл, который загружается в папку Загрузки пользователя.

Затем предполагается распаковать zip-файл.

Вот в чем проблема – хотя все это происходит в одной функции и код распаковки выполняется после кода записи, он всегда пытается распаковать ПЕРЕД тем, как ZipOutputStream завершит создание zip-файла, и, таким образом, распаковка никогда не происходит.

Я подтвердил, что это так, добавив условие wait(), чтобы заставить его ждать указанное время после начала записи в ZipOutputStream, но было бы наилучшим решением сделать это программно, чтобы завершение записи в zip-файл служило сигналом для начала распаковки.

Это мой код на данный момент:

public void downloadDocuments(HttpServletResponse response) throws IOException, AuthException {

    //Добавьте файлы в ZipOutputStream и сохраните как zip-файл
    response.setContentType("application/zip");
    response.setHeader("Content-Disposition", "attachment; filename=\"allfiles.zip\"");
    ZipOutputStream zipOutput = null;

    try {
        zipOutput = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));
        List<SupportingDocEntity> docs = documentDao.getDocuments();

        for (int ii=0; ii<docs.size(); ii++) {                
            SupportingDocEntity docEntity = docs.get(ii);
            ZipEntry zipEntry = new ZipEntry(docEntity.getFileName() + docEntity.getFileExtension());
            zipOutput.putNextEntry(new ZipEntry(zipEntry));
            zipOutput.write(docEntity.getContent());
            zipOutput.closeEntry();
        }

    } finally {
        if (zipOutput != null) try { zipOutput.close(); } catch (IOException logOrIgnore) { /**/ }
    }

    //Как мне заставить его ждать, пока zip-файл полностью не будет создан, прежде чем выполнять код ниже???

    //Распаковка вновь созданного zip-файла
    byte[] buffer = new byte[2048];
    ZipInputStream zipInput = new ZipInputStream(new FileInputStream("C:/Users/mydirectory/Downloads/allfiles.zip"));
    try {
        ZipEntry zipEntry;
        while((zipEntry = zipInput.getNextEntry())!=null) {

            String outpath = "C:/Users/mydirectory/Downloads/" + zipEntry.getName();
            FileOutputStream fileOutput = null;
            try {
                fileOutput = new FileOutputStream(outpath);
                int len = 0;
                while ((len = zipInput.read(buffer)) > 0) {
                    fileOutput.write(buffer, 0, len);
                }
            }
            finally {
                // мы всегда должны закрывать выходной файл
                if(fileOutput!=null) fileOutput.close();
            }
        }
    }
    finally {
        // мы всегда должны закрывать zip-файл.
        zipInput.close();
    }
}

Заранее спасибо

Ответ или решение

Для корректной работы с ZipOutputStream и последующей распаковкой созданного zip-файла, необходимо убедиться, что запись в поток завершена до начала процесса распаковки. В вашем коде это может быть достигнуто путем упорядочивания операций и исключения асинхронного поведения. Вот исправленный код с комментариями:

public void downloadDocuments(HttpServletResponse response) throws IOException, AuthException {
    // Установка заголовков для ответа
    response.setContentType("application/zip");
    response.setHeader("Content-Disposition", "attachment; filename=\"allfiles.zip\"");

    ZipOutputStream zipOutput = null;

    try {
        // Инициализация ZipOutputStream
        zipOutput = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));
        List<SupportingDocEntity> docs = documentDao.getDocuments();

        // Добавление документов в zip-файл
        for (SupportingDocEntity docEntity : docs) {
            ZipEntry zipEntry = new ZipEntry(docEntity.getFileName() + docEntity.getFileExtension());
            zipOutput.putNextEntry(zipEntry);
            zipOutput.write(docEntity.getContent());
            zipOutput.closeEntry();
        }

    } finally {
        // Закрываем ZipOutputStream, чтобы завершить запись
        if (zipOutput != null) try { zipOutput.close(); } catch (IOException e) { /* Обработка исключений */ }
    }

    // После завершения записи в zip-файл можно выполнять его распаковку
    unzipFile("C:/Users/mydirectory/Downloads/allfiles.zip");
}

// Метод для распаковки zip-файла
private void unzipFile(String zipFilePath) throws IOException {
    byte[] buffer = new byte[2048];
    ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFilePath));

    try {
        ZipEntry zipEntry;
        while ((zipEntry = zipInput.getNextEntry()) != null) {
            String outpath = "C:/Users/mydirectory/Downloads/" + zipEntry.getName();
            FileOutputStream fileOutput = null;
            try {
                fileOutput = new FileOutputStream(outpath);
                int len;
                while ((len = zipInput.read(buffer)) > 0) {
                    fileOutput.write(buffer, 0, len);
                }
            } finally {
                if (fileOutput != null) fileOutput.close(); // Закрытие выходного потока после записи
            }
        }
    } finally {
        zipInput.close(); // Закрытие ZipInputStream
    }
}

Что было изменено:

  1. Порядок операций: Убедитесь, что распаковка zip-файла выполняется только после закрытия ZipOutputStream. Я вынес процесс распаковки в отдельный метод unzipFile, который вызывается только после завершения всех операций с zip-файлом.

  2. Закрытие потоков: Все потоки (как входные, так и выходные) закрываются в блоке finally, что гарантирует, что они будут закрыты в случае исключения, тем самым предотвращая утечки ресурсов.

Теперь код следует правильной логике, и распаковка zip-файла будет выполняться только после того, как файл полностью создан и записан.

Оцените материал
Добавить комментарий

Капча загружается...