Вопрос или проблема
У меня есть 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
}
}
Что было изменено:
-
Порядок операций: Убедитесь, что распаковка zip-файла выполняется только после закрытия
ZipOutputStream
. Я вынес процесс распаковки в отдельный методunzipFile
, который вызывается только после завершения всех операций с zip-файлом. -
Закрытие потоков: Все потоки (как входные, так и выходные) закрываются в блоке
finally
, что гарантирует, что они будут закрыты в случае исключения, тем самым предотвращая утечки ресурсов.
Теперь код следует правильной логике, и распаковка zip-файла будет выполняться только после того, как файл полностью создан и записан.