Чтобы сжать большой файл в ZIP с помощью Java

Мне нужно сжать один большой файл (~ 450 Мбайт) через класс Java ZipOutputStream. Это большое измерение вызывает проблему ошибки «OutOfMemory» моего пространства кучи JVM. Это происходит потому, что метод "zos.write(...)" сохраняет ВСЕ содержимое файла для сжатия во внутреннем массиве байтов перед его сжатием.

            origin = new BufferedInputStream(fi, BUFFER);
        ZipEntry entry = new ZipEntry(filePath);
        zos.putNextEntry(entry);

        int count;
        while ((count = origin.read(data, 0, BUFFER)) != -1)
        {
            zos.write(data, 0, count);
        }
        origin.close();

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

кто-нибудь имеет представление об этом?


person robob    schedule 20.11.2009    source источник


Ответы (4)


Согласно вашему комментарию к ответу Сэма, вы, очевидно, создали ZipOutputStream, который обертывает ByteArrayOutputStream. ByteArrayOutputStream, конечно же, кэширует сжатый результат в памяти. Если вы хотите, чтобы он был записан на диск, вы должны обернуть ZipOutputStream вокруг FileOutputStream.

person jarnbjo    schedule 20.11.2009
comment
Хорошо, я понимаю, что вы сказали мне, но сжатые данные составляют около 60 МБ ... этого мало, чтобы запустить ошибку кучи OutOfSpace. Что насчет этого? Я должен установить Xmx1024m, чтобы быть хорошим! Наверное это моя ошибка! - person robob; 20.11.2009
comment
+1, используйте FileOutputStream для записи zip на диск или, если вы хотите передать его прямо в браузер, используйте HttpServletResponse outputStream. - person Sam Barnum; 20.11.2009
comment
Когда 60 МБ памяти израсходовали, вы использовали настройки JVM по умолчанию? Если это так, то это звучит примерно так. Даже если ваша JVM работает с размером кучи 64 M, в какой-то момент ByteArrayOutputStream потребуется расширить этот массив byte[]... что означает полную копию. - person PSpeed; 21.11.2009

Существует библиотека под названием TrueZip, которую я успешно использовал в прошлом. делать такие вещи.

Я не могу гарантировать, что буферизация будет лучше. Я знаю, что он делает много вещей с помощью собственного кода, а не зависит от Zip API JDK.

Так что попробовать стоит, на мой взгляд.

person Carl Smotricz    schedule 20.11.2009

ZipOutputStream основан на потоке, он не удерживает память. Ваш БУФЕР может быть слишком большим.

person Sam Barnum    schedule 20.11.2009
comment
Мой буфер составляет 2048 байт, и я не думаю, что он слишком большой! Это исключение: исключение в потоке main java.lang.OutOfMemoryError: пространство кучи Java в java.util.Arrays.copyOf(Arrays.java:2786) в java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94) в java .util.zip.DeflaterOutputStream.deflate(DeflaterOutputStream.java:161) в java.util.zip.DeflaterOutputStream.write(DeflaterOutputStream.java:118) в java.util.zip.ZipOutputStream.write(ZipOutputStream.java:272) - person robob; 20.11.2009

Интересно, это потому, что вы храните содержимое в ZipEntry, возможно, оно в основном загружает все свое содержимое перед записью ZipEntry. Обязательно ли использовать Zip? Если вам нужно сжать только один поток данных, вы можете вместо этого заглянуть в GZIPOutputStream. Я считаю, что у него не было бы той же проблемы.

Надеюсь это поможет.

person cjstehno    schedule 20.11.2009
comment
Мне нужно сохранить содержимое каталога в Zip-файле для отправки через веб-службу. - person robob; 20.11.2009
comment
Звучит как плохая идея, если в вашем ответе есть такие большие объекты. Рассмотрите вместо этого возврат URL-адреса, откуда можно получить zip-файл. Обычные сервлеты допускают потоковый ответ на основе байтов. - person Thorbjørn Ravn Andersen; 20.11.2009