Добавление файлов к zip-файлу с Java

Я в настоящее время извлекаю содержание военного файла и затем добавляю некоторые новые файлы к структуре каталогов и затем создаю новый военный файл.

Это все сделано программно от Java - но я задаюсь вопросом, не было ли более эффективно скопировать военный файл и затем просто добавить файлы - затем я не должен был бы ожидать, пока война расширяется и затем должна быть сжата снова.

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

Кто-либо может дать некоторые подсказки или указатели?

ОБНОВЛЕНИЕ:

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

Кто-либо имеет опыт или обратную связь на TrueZip или может рекомендовать другой подобный libaries?

56
задан Christian Schlichtherle 27 July 2011 в 15:04
поделиться

5 ответов

Смотрите этот отчет об ошибке .

Использование режима добавления для любого вида структурированные данные, такие как zip-файлы или tar файлы - это не то, что ты можешь на самом деле рассчитывать на работу. Эти форматы файлов иметь внутренний "конец файла" индикация, встроенная в формат данных.

Если вы действительно хотите пропустить промежуточный этап ненанесения/восстановления, вы можете прочитать файл войны, получить все записи zip, а затем записать в новый файл войны "добавление" новых записей, которые вы хотите добавить. Не идеальное, но, по крайней мере, более автоматизированное решение.

1
ответ дан 26 November 2019 в 17:04
поделиться

Я не знаю Java библиотека, которая делает то, что вы описываете. Но то, что вы описали, практично. Вы можете сделать это в .NET, используя DotNetZip .

Майкл Крауклис прав в том, что вы не можете просто «добавить» данные в военный файл или zip-файл, но это не потому, что, строго говоря, в военном файле есть указание на «конец файла». Это потому, что формат war (zip) включает в себя каталог, который обычно присутствует в конце файла, который содержит метаданные для различных записей в файле war. Наивное добавление к файлу войны не приводит к обновлению каталога, поэтому у вас просто есть файл войны с добавленным к нему мусором.

Что необходимо, так это интеллектуальный класс, который понимает формат и может читать + обновлять военный файл или zip-файл, включая соответствующий каталог. DotNetZip делает это без распаковки / повторного сжатия неизмененных записей, как вы описали или пожелали.

2
ответ дан 26 November 2019 в 17:04
поделиться

Когда-то у меня было подобное требование, но оно касалось чтения и записи zip-архивов (формат .war должен быть похожим). Я попытался сделать это с существующими потоками Java Zip, но нашел часть записи громоздкой, особенно когда задействованы каталоги.

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

25
ответ дан 26 November 2019 в 17:04
поделиться

Как говорит Чизо, это невозможно сделать. AFAIK внешние интерфейсы на молнии делают то же самое, что и вы внутри.

В любом случае, если вас беспокоит скорость извлечения / сжатия всего, вы можете попробовать библиотеку SevenZipJBindings .

Я рассказывал об этой библиотеке в моем блоге несколько месяцев назад (извините за автоматическое продвижение). В качестве примера, извлечение zip-файла размером 104 МБ с использованием java.util.zip заняло у меня 12 секунд, в то время как использование этой библиотеки заняло 4 секунды.

В обеих ссылках вы можете найти примеры того, как его использовать.

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

2
ответ дан 26 November 2019 в 17:04
поделиться

Как уже упоминалось, невозможно добавить контент в существующий zip-архив (или войну). Однако можно создать новый zip-архив на лету без временной записи извлеченного содержимого на диск. Трудно предположить, насколько это будет быстрее, но это самое быстрое, что вы можете получить (по крайней мере, насколько мне известно) со стандартной Java. Как упоминал Карлос Тасада, SevenZipJBindings может выжать несколько лишних секунд, но перенос этого подхода на SevenZipJBindings все равно будет быстрее, чем использование временных файлов с той же библиотекой.

Вот код, который записывает содержимое существующего zip-архива (war.zip) и добавляет дополнительный файл (answer.txt) в новый zip-архив (append.zip). Все, что требуется, - это Java 5 или новее, никаких дополнительных библиотек не требуется.

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class Main {

    // 4MB buffer
    private static final byte[] BUFFER = new byte[4096 * 1024];

    /**
     * copy input to output stream - available in several StreamUtils or Streams classes 
     */    
    public static void copy(InputStream input, OutputStream output) throws IOException {
        int bytesRead;
        while ((bytesRead = input.read(BUFFER))!= -1) {
            output.write(BUFFER, 0, bytesRead);
        }
    }

    public static void main(String[] args) throws Exception {
        // read war.zip and write to append.zip
        ZipFile war = new ZipFile("war.zip");
        ZipOutputStream append = new ZipOutputStream(new FileOutputStream("append.zip"));

        // first, copy contents from existing war
        Enumeration<? extends ZipEntry> entries = war.entries();
        while (entries.hasMoreElements()) {
            ZipEntry e = entries.nextElement();
            System.out.println("copy: " + e.getName());
            append.putNextEntry(e);
            if (!e.isDirectory()) {
                copy(war.getInputStream(e), append);
            }
            append.closeEntry();
        }

        // now append some extra content
        ZipEntry e = new ZipEntry("answer.txt");
        System.out.println("append: " + e.getName());
        append.putNextEntry(e);
        append.write("42\n".getBytes());
        append.closeEntry();

        // close
        war.close();
        append.close();
    }
}
44
ответ дан 26 November 2019 в 17:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: