Распаковка zip архивов java

Содержание
  1. Rukovodstvo
  2. статьи и идеи для разработчиков программного обеспечения и веб-разработчиков.
  3. Работа с Zip-файлами в Java
  4. Введение В этой статье я расскажу об основах создания, взаимодействия, проверки и извлечения файлов zip-архива с помощью Java (в частности, OpenJDK 11). Пример кода, используемый в этой статье, имеет форму проекта Gradle и размещен в этом репозитории GitHub [https://github.com/amcquistan/zip-files-in-java], чтобы вы могли запускать и экспериментировать. Будьте осторожны при изменении кода, удаляющего файлы. Как уже упоминалось, приведенные здесь примеры кода написаны с использованием Java 11 и util
  5. Вступление
  6. Ключевые классы Java для работы с Zip-архивами
  7. Общие пути к файлам для примеров кода
  8. Проверка содержимого Zip-архива
  9. Извлечение Zip-архива
  10. Запись файлов непосредственно в новый Zip-архив
  11. Архивирование существующего файла в новый Zip-архив
  12. Архивирование папки в новый ZIP-архив
  13. Заключение
  14. Распаковка zip-архивов
  15. Обработка директории
  16. Обработка файла
  17. Распаковка zip архивов java
  18. ZipOutputStream. Запись архивов
  19. Чтение архивов. ZipInputStream

Rukovodstvo

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

Работа с Zip-файлами в Java

Введение В этой статье я расскажу об основах создания, взаимодействия, проверки и извлечения файлов zip-архива с помощью Java (в частности, OpenJDK 11). Пример кода, используемый в этой статье, имеет форму проекта Gradle и размещен в этом репозитории GitHub [https://github.com/amcquistan/zip-files-in-java], чтобы вы могли запускать и экспериментировать. Будьте осторожны при изменении кода, удаляющего файлы. Как уже упоминалось, приведенные здесь примеры кода написаны с использованием Java 11 и util

Вступление

В этой статье я расскажу об основах создания, взаимодействия, проверки и извлечения файлов zip-архива с помощью Java (в частности, OpenJDK 11). Образец кода, используемый в этой статье, представляет собой проект Gradle и размещен в этом репозитории GitHub, чтобы вы могли запускать и экспериментировать. Будьте осторожны при изменении кода, удаляющего файлы.

Как уже упоминалось, приведенные здесь примеры кода написаны с использованием Java 11 и используют var которое было введено в Java 10, и парадигмы функционального программирования в Java 8, поэтому для их запуска требуется минимальная версия Java 10.

Читайте также:  Как найти нод двух чисел питон

Ключевые классы Java для работы с Zip-архивами

Я считаю, что неплохо начать с определения некоторых известных классов, которые обычно используются при работе с zip-архивами в Java. Эти классы java.nio.file пакетах java.util.zip или java.nio.file.

  • java.util.zip.ZipFile используется для чтения и взаимодействия с элементами ( ZipEntry ) в zip-архиве
  • java.util.zip.ZipEntry — это абстракция, представляющая такой элемент, как файл или каталог в zip-архиве (т. е. экземпляр ZipFile
  • java.util.zip.ZipOutputStream — это реализация абстрактного класса OutputStream, который используется для записи элементов в Zip-файл.
  • java.nio.file.Files — очень удобный класс утилит для потоковой передачи и копирования файловых данных в экземпляры ZipOutputStream или из экземпляров ZipFile.
  • java.nio.file.Path — еще один удобный класс утилит для эффективной работы с путями к файлам

Общие пути к файлам для примеров кода

В примере кода я использую два общих каталога для записи и чтения данных в / из которых оба относятся к корню проекта Gradle. Взгляните на связанный репо во введении или, что еще лучше, запустите образцы. Просто помните об этих двух переменных Path, поскольку они часто используются в качестве начального каталога для входов и выходов.

Проверка содержимого Zip-архива

Вы можете создать экземпляр ZipFile и передать ему путь к существующему zip-архиву, который, по сути, открывает его, как любой другой файл, а затем проверяет содержимое, запрашивая ZipEntry содержащееся внутри него. Обратите внимание на то, что ZipFile реализует интерфейс AutoCloseable , что делает его отличным кандидатом на конструкцию программирования Java «попытка с ресурсами», показанную ниже и во всех приведенных здесь примерах.

 static void showZipContents() < try (var zf = new ZipFile("ZipToInspect.zip")) < System.out.println(String.format("Inspecting contents of: %s\n", zf.getName())); EnumerationzipEntries = zf.entries(); zipEntries.asIterator().forEachRemaining(entry -> < System.out.println(String.format( "Item: %s \nType: %s \nSize: %d\n", entry.getName(), entry.isDirectory() ? "directory" : "file", entry.getSize() )); >); > catch (IOException e) < e.printStackTrace(); >> 

Запуск проекта Gradle с использованием следующего:

Читайте также:  Java thread pool executor queue

Это дает результат для метода App.showZipContents

 > Task :run Inspecting contents of: ZipToInspect.zip Item: ZipToInspect/ Type: directory Size: 0 Item: ZipToInspect/greetings.txt Type: file Size: 160 Item: ZipToInspect/InnerFolder/ Type: directory Size: 0 Item: ZipToInspect/InnerFolder/About.txt Type: file Size: 39 

Здесь вы можете видеть, что это распечатывает все файлы и каталоги в zip-архиве, даже файлы в каталогах.

Извлечение Zip-архива

Извлечение содержимого zip-архива на диск не требует ничего, кроме репликации той же структуры каталогов, что и внутри ZipFile , которую можно определить с помощью ZipEntry.isDirectory а затем копирования файлов, представленных в ZipEntry на диск.

 static void unzipAZip() < var outputPath = Path.of("UnzippedContents"); try (var zf = new ZipFile("ZipToInspect.zip")) < // Delete if exists, then create a fresh empty directory to put the zip archive contents initialize(outputPath); EnumerationzipEntries = zf.entries(); zipEntries.asIterator().forEachRemaining(entry -> < try < if (entry.isDirectory()) < var dirToCreate = outputPath.resolve(entry.getName()); Files.createDirectories(dirToCreate); >else < var fileToCreate = outputPath.resolve(entry.getName()); Files.copy(zf.getInputStream(entry), fileToCreate); >> catch(IOException ei) < ei.printStackTrace(); >>); > catch(IOException e) < e.printStackTrace(); >> 

Запись файлов непосредственно в новый Zip-архив

Поскольку запись zip-архива на самом деле является не чем иным, как записью потока данных в какое-то место назначения (в данном случае Zip-файл), запись данных, таких как String, в zip-архив, отличается только тем, что вам нужно сопоставить данные, которые записано в ZipEntry добавленные в ZipOutputStream .

Опять же, ZipOutputStream реализует AutoCloseable , поэтому его лучше всего использовать с оператором try-with-resources. Единственная реальная проблема — не забыть закрыть свой ZipEntry когда вы закончите с каждым из них, чтобы было ясно, когда он больше не должен получать данные.

 static void zipSomeStrings() < MapstringsToZip = Map.ofEntries( entry("file1", "This is the first file"), entry("file2", "This is the second file"), entry("file3", "This is the third file") ); var zipPath = zippedDir.resolve("ZipOfStringData.zip"); try (var zos = new ZipOutputStream( new BufferedOutputStream(Files.newOutputStream(zipPath)))) < for (var entry : stringsToZip.entrySet()) < zos.putNextEntry(new ZipEntry(entry.getKey())); zos.write(entry.getValue().getBytes()); zos.closeEntry(); >> catch (IOException e) < e.printStackTrace(); >> 

Архивирование существующего файла в новый Zip-архив

Если вы ранее копировали файл на Java, то вы уже являетесь ПРОФЕССИОНАЛОМ в создании zip-архива из существующего файла (или каталога, если на то пошло). Опять же, единственное реальное отличие состоит в том, что вам нужно проявить дополнительную осторожность, чтобы убедиться, что вы сопоставляете файлы с соответствующими экземплярами ZipEntry

В этом примере я создаю входной файл «FileToZip.txt» и записываю в него данные «Привет, друзья Java!» а затем используйте Files.copy (Path, OutputStream), чтобы связать ZipEntry с файлом FileToZip.txt внутри zip-архива ZippedFile.zip, который я создаю с экземпляром ZipOutoutStream

 static void zipAFile() < var inputPath = inputDataDir.resolve("FileToZip.txt"); var zipPath = zippedDir.resolve("ZippedFile.zip"); try (var zos = new ZipOutputStream( new BufferedOutputStream(Files.newOutputStream(zipPath)))) < Files.writeString(inputPath, "Howdy There Java Friends!\n"); zos.putNextEntry(new ZipEntry(inputPath.toString())); Files.copy(inputPath, zos); zos.closeEntry(); >catch (IOException e) < e.printStackTrace(); >> 

Архивирование папки в новый ZIP-архив

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

В этом примере я создаю каталог с именем «foldertozip», содержащий структуру, показанную ниже, а затем заархивирую его в zip-архив.

 tree . . └── foldertozip ├── emptydir ├── file1.txt └── file2.txt 

В следующем коде обратите внимание на то, что я использую метод Files.walk(Path) для обхода дерева каталогов «foldertozip» и поиска пустых каталогов («emptydir» в этом примере), и если / когда обнаружен, я присоединяю разделитель каталогов к имя в ZipEntry . После этого я закрываю его, как только добавляю в экземпляр ZipOutputStream

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

 static void zipADirectoryWithFiles() < var foldertozip = inputDataDir.resolve("foldertozip"); var dirFile1 = foldertozip.resolve("file1.txt"); var dirFile2 = foldertozip.resolve("file2.txt"); var zipPath = zippedDir.resolve("ZippedDirectory.zip"); try (var zos = new ZipOutputStream( new BufferedOutputStream(Files.newOutputStream(zipPath)))) < Files.createDirectory(foldertozip); Files.createDirectory(foldertozip.resolve("emptydir")); Files.writeString(dirFile1, "Does this Java get you rev'd up or what?"); Files.writeString(dirFile2, "Java Java Java . Buz Buz Buz!"); Files.walk(foldertozip).forEach(path -> < try < var reliativePath = inputDataDir.relativize(path); var file = path.toFile(); if (file.isDirectory()) < var files = file.listFiles(); if (files == null || files.length == 0) < zos.putNextEntry(new ZipEntry( reliativePath.toString() + File.separator)); zos.closeEntry(); >> else < zos.putNextEntry(new ZipEntry(reliativePath.toString())); zos.write(Files.readAllBytes(path)); zos.closeEntry(); >> catch(IOException e) < e.printStackTrace(); >>); > catch(IOException e) < e.printStackTrace(); >> 

Заключение

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

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

Licensed under CC BY-NC-SA 4.0

Источник

Распаковка zip-архивов

Ранее мы рассматривали Создание zip-архивов, а теперь попробуем выполнить обратную операцию. Если у нас имеется zip-архив с файлами, то распаковать его мы можем следующим образом:

public static void main(String[] args) throws IOException <
try ( var file = new ZipFile( «/home/devmark/Documents/test.zip» )) <
var entries = file.entries();
var uncompressedDirectory = new File(file.getName()).getParent() + File.separator;
while (entries.hasMoreElements()) <
var entry = entries.nextElement();
if (entry.isDirectory()) <
processDirectory(uncompressedDirectory, entry);
> else <
processFile(file, uncompressedDirectory, entry);
>
>
>
>

Мы создаём объект ZipFile из пакета java.util, в конструктор которому передаём полный путь до нашего архива. Все действия выполняем в конструкции try-with-resources, которая автоматически закроет ресурс, связанный с нашим архивом, в конце работы с ним.

Метод entries() предоставляет список содержащихся в архиве сущностей (файлов и папок). Затем мы можем обходить эти сущности при помощи цикла while и метода hasMoreElements(). Распакованный архив будем складывать в ту же директорию, где лежит исходный файл с архивом.

В цикле мы проверяем у каждой сущности признак isDirectory() и в зависимости от этого либо создаём подпапки в папке назначения, либо распаковываем содержащийся в архиве файл. Ниже приведены оба метода: processDirectory() и processFile().

Обработка директории

private static void processDirectory(String uncompressedDirectory, ZipEntry entry) <
var newDirectory = uncompressedDirectory + entry.getName();
System.out.println( «Creating Directory: » + newDirectory);
var directory = new File(newDirectory);
if (!directory.exists()) <
directory.mkdirs();
>
>

Тут мы прибавляем к нашему базовому пути имя папки, содержащейся в архиве. Затем создаём объект File, асоциированный с этой новой папкой и проверяем её существование. Если её нет (что вполне ожидаемо), то создаем все папки с подпапками, содержащиеся в данном пути с помощью метода mkdirs().

Обработка файла

private static void processFile(ZipFile file, String uncompressedDirectory, ZipEntry entry) throws IOException <
try (
var is = file.getInputStream(entry);
var bis = new BufferedInputStream(is)
) <
var uncompressedFileName = uncompressedDirectory + entry.getName();
try (
var os = new FileOutputStream(uncompressedFileName);
var bos = new BufferedOutputStream(os)
) <
while (bis.available() > 0 ) <
bos.write(bis.read());
>
>
>
System.out.println( «Written: » + entry.getName());
>

В конструкции try-with-resources мы создаём поток InputStream, связанный с упакованным файлом внутри архива. Затем оборачиваем его буферизованным потоком BufferedInputStream, который минимизирует количество операций с файловой системой, что заметно ускоряет процесс распаковки.

Затем создаём аналогично поток FileOutputStream связанный с файлом, который мы собираемся записать. Оборачиваем его также в BufferedOutputStream для ускорения и в цикле пишем распакованный файл с помощью метода write().

Источник

Распаковка zip архивов java

Кроме общего функционала для работы с файлами Java предоставляет функциональность для работы с таким видом файлов как zip-архивы. Для этого в пакете java.util.zip определены два класса — ZipInputStream и ZipOutputStream

ZipOutputStream. Запись архивов

Для создания архива используется класс ZipOutputStream. Для создания объекта ZipOutputStream в его конструктор передается поток вывода:

ZipOutputStream(OutputStream out)

Для записи файлов в архив для каждого файла создается объект ZipEntry , в конструктор которого передается имя архивируемого файла. А чтобы добавить каждый объект ZipEntry в архив, применяется метод putNextEntry() .

import java.io.*; import java.util.zip.*; public class Program < public static void main(String[] args) < String filename = "notes.txt"; try(ZipOutputStream zout = new ZipOutputStream(new FileOutputStream("output.zip")); FileInputStream fis= new FileInputStream(filename);) < ZipEntry entry1=new ZipEntry("notes.txt"); zout.putNextEntry(entry1); // считываем содержимое файла в массив byte byte[] buffer = new byte[fis.available()]; fis.read(buffer); // добавляем содержимое к архиву zout.write(buffer); // закрываем текущую запись для новой записи zout.closeEntry(); >catch(Exception ex) < System.out.println(ex.getMessage()); >> >

После добавления объекта ZipEntry в поток нам также надо добавить в него и содержимое файла. Для этого используется метод write, записывающий в поток массив байтов: zout.write(buffer); . В конце надо закрыть ZipEntry с помощью метода closeEntry() . После этого можно добавлять в архив новые файлы — в этом случае все вышеописанные действия для каждого нового файла повторяются.

Чтение архивов. ZipInputStream

Для чтения архивов применяется класс ZipInputStream . В конструкторе он принимает поток, указывающий на zip-архив:

ZipInputStream(InputStream in)

Для считывания файлов из архива ZipInputStream использует метод getNextEntry() , который возвращает объект ZipEntry . Объект ZipEntry представляет отдельную запись в zip-архиве. Например, считаем какой-нибудь архив:

import java.io.*; import java.util.zip.*; public class Program < public static void main(String[] args) < try(ZipInputStream zin = new ZipInputStream(new FileInputStream("output.zip"))) < ZipEntry entry; String name; while((entry=zin.getNextEntry())!=null)< name = entry.getName(); // получим название файла System.out.printf("File name: %s \n", name); // распаковка FileOutputStream fout = new FileOutputStream("new" + name); for (int c = zin.read(); c != -1; c = zin.read()) < fout.write(c); >fout.flush(); zin.closeEntry(); fout.close(); > > catch(Exception ex) < System.out.println(ex.getMessage()); >> >

ZipInputStream в конструкторе получает ссылку на поток ввода. И затем в цикле выводятся все файлы и их размер в байтах, которые находятся в данном архиве.

Затем данные извлекаются из архива и сохраняются в новые файлы, которые находятся в той же папке и которые начинаются с «new».

Источник

Оцените статью