Java Чтение из n-вложенных zip-архивов, модификация и запись в новый zip-файл с сохранением оригинальной структуры - PullRequest
0 голосов
/ 03 ноября 2019

Я уже давно борюсь с этой проблемой. Кажется, что все вопросы здесь, в SO или интернете, работают только над «неглубокими» структурами с одним почтовым индексом внутри другого. Однако у меня есть zip-архив, структура которого более или менее примерно такая:

input.zip/ --1.zip/ --folder/ ----2.zip/ ------3.zip/ --------test/ ----------some-other-folder/ ----------archive.gz/ ------------filte-to-parse ----------file-to-parse3.txt ------file-to-parse.txt --4.zip/ ------folder/ и т. Д., мой код должен обрабатывать N-уровень zips при сохранении оригинальных zip-архивов. , gzips, папки и структура файлов. Использование временных файлов запрещено из-за отсутствия привилегий (это то, что я не хочу менять).

Это мой код, который я написал до сих пор, однако ZipOutputStream, кажется, работает только с одним (top) уровень - в случае каталогов с одинаковыми именами файлов / каталогов он выдает Exception in thread "main" java.util.zip.ZipException: duplicate entry: folder/. Он также пропускает пустые каталоги (что не ожидается). Чего я хочу добиться, так это как-то переместить мой ZipOutputStream на «более низкий» уровень и выполнять операции с каждым из почтовых индексов. Может быть, есть лучший подход для решения этой проблемы, любая помощь будет оценена. Мне нужно выполнить определенное извлечение / модификацию текста позже, однако я пока не запускаю его, пока чтение / запись всей структуры не будет работать должным образом. Заранее благодарю за любую помощь!

    //constructor
private final File zipFile;

ArchiveResolver(String fileToHandle) {
    this.zipFile = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(fileToHandle)).getFile());
}

void resolveInputFile() throws Exception {
    FileInputStream fileInputStream = new FileInputStream(this.zipFile);
    FileOutputStream fileOutputStream = new FileOutputStream("out.zip");
    ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
    ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);

    zip(zipInputStream, zipOutputStream);

    zipInputStream.close();
    zipOutputStream.close();
}

//    this one doesn't preserve internal structure(empty folders), but can work on each file
private void zip(ZipInputStream zipInputStream, ZipOutputStream zipOutputStream) throws IOException {
    ZipEntry entry;
    while ((entry = zipInputStream.getNextEntry()) != null) {
        System.out.println(entry.getName());
        byte[] buffer = new byte[1024];
        int length;
        if (entry.getName().endsWith(".zip")) {
//              wrapping outer zip streams to inner streams making actual entries a new source
            ZipInputStream innerZipInputStream = new ZipInputStream(zipInputStream);
            ZipOutputStream innerZipOutputStream = new ZipOutputStream(zipOutputStream);

            ZipEntry zipEntry = new ZipEntry(entry.getName());
//              add new zip entry here to outer zipOutputStream: i.e. data.zip
            zipOutputStream.putNextEntry(zipEntry);

//              now treat this data.zip as parent and call recursively zipFolder on it
            zip(innerZipInputStream, innerZipOutputStream);

//              Finish internal stream work when innerZipOutput is done
            innerZipOutputStream.finish();

//              Close entry
            zipOutputStream.closeEntry();
        } else if (entry.isDirectory()) {
//              putting new zip entry into output stream and adding extra '/' to make
//              sure zipOutputStream will treat it as folder
            ZipEntry zipEntry = new ZipEntry(entry.getName() + "/");

//              this only should preserve internal structure
            zipOutputStream.putNextEntry(zipEntry);

//              reading everything from zipInputStream
            while ((length = zipInputStream.read(buffer)) > 0) {
//                  sending it straight to zipOutputStream
                zipOutputStream.write(buffer, 0, length);
            }

            zipOutputStream.closeEntry();

//              This else will include checking if file is respectively:
//              .gz file <- then open it, read from file inside, modify and save it
//              .txt file <- also read, modify and preserve
        } else {
//              create new entry on top of this
            ZipEntry zipEntry = new ZipEntry(entry.getName());
            zipOutputStream.putNextEntry(zipEntry);
            while ((length = zipInputStream.read(buffer)) > 0) {
                zipOutputStream.write(buffer, 0, length);
            }
            zipOutputStream.closeEntry();
        }
    }
}

//    This one preserves internal structure (empty folders and so)
//    BUT! no work on each file is possible it just preserves everything as it is
private void zipWhole(ZipInputStream zipInputStream, ZipOutputStream zipOutputStream) throws IOException {
    ZipEntry entry;
    while ((entry = zipInputStream.getNextEntry()) != null) {
        System.out.println(entry.getName());
        byte[] buffer = new byte[1024];
        int length;
        zipOutputStream.putNextEntry(new ZipEntry(entry.getName()));
        while ((length = zipInputStream.read(buffer)) > 0) {
            zipOutputStream.write(buffer, 0, length);
        }
        zipOutputStream.closeEntry();
    }
}

РЕДАКТИРОВАТЬ:

Обновил мой код до последней версии, все еще нечем гордиться, но сделал некоторые изменения, однако все еще не работает ... ЯЯ добавил сюда два очень важных комментария о (на мой взгляд) коде, который не работает. Итак, я протестировал два подхода - первый получает ZipInputStream из zipFile с помощью getInputStream(ZipEntry e); - выдает Exception in thread "main" java.util.zip.ZipException: no current ZIP entry, когда я пытаюсь поместить некоторые записи в ZipOutputStream. Второй подход фокусируется на "оборачивании" ZipInputStream друг в друга -> это приводит к пустым ZipInputStream s без записей, и приложение просто просматривает файлы, перечисляет их (только верхний уровень zips ...) и завершает безсохранив что-нибудь в файл out.zip.

РЕДАКТИРОВАТЬ 2:

С небольшими предложениями от людей в комментариях я решил переписать свой код с упором на close, finish и closeEntryв соответствующих местах (надеюсь, я сделал это лучше сейчас). Итак, сейчас я кое-чего достиг - код перебирает каждую запись и сохраняет ее в файле out.zip с соответствующей упаковкой zip внутри. Тем не менее пропускает пустые папки, но не знаю почему (я проверил некоторые вопросы по стеку и в Интернете, кажется, все в порядке). В любом случае, спасибо за помощь, я постараюсь решить эту проблему и буду постоянно обновлять.

1 Ответ

0 голосов
/ 03 ноября 2019

Кажется, что там много работы по отладке и рефакторингу.

Существует очевидная проблема, заключающаяся в том, что вы не закрываете свои потоки / записи или делаете это в неправильном порядке. Буферизованные данные будут потеряны, а центральный каталог не записан. (Существует сложность, заключающаяся в том, что потоки Java бесполезно закрывают поток, который они обертывают, поэтому существует finish против close, но это все равно необходимо сделать в правильном порядке.)

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

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

...