Java file in zip archive

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

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

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

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

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

  • java.util.zip.ZipFile используется для чтения и взаимодействия с элементами ( экземплярами ZipEntry ) в zip-архиве
  • java.util.zip.ZipEntry – это абстракция, представляющая элемент, такой как файл или каталог в zip-архиве (т. е. ZipFile экземпляр)
  • java.util.zip.ZipOutputStream является реализацией абстрактного класса OutputStream и используется для записи элементов в Zip-файл
  • java.nio.файл.Файлы – это очень удобный класс утилит для потоковой передачи и копирования файловых данных в экземпляры ZipOutputStream или из экземпляров ZipFile
  • java.nio.файл.Путь еще один удобный класс утилит для эффективной работы с путями к файлам
Читайте также:  margin-top

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

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

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

Вы можете создать экземпляр класса ZipFile и передать ему путь к существующему zip-архиву, который, по сути, открывает его, как и любой другой файл, а затем проверить содержимое, запросив перечисление ZipEntry , содержащееся в нем. Обратите внимание, что Zip-файл реализует Автоклавируемый интерфейс, что делает его отличным кандидатом для попробуйте с ресурсами 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 с использованием следующих:

Это дает результат для приложения.показать содержимое Zip метод:

> 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.IS , а затем копирование файлов, представленных в экземплярах 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-файл), то запись данных, например строковых данных, в zip-архив отличается только тем, что вам нужно сопоставить данные, записываемые в ZipEntry экземпляры, добавленные в ZipOutputStream .

Опять же, ZipOutputStream реализует автоклавируемый интерфейс, поэтому его лучше всего использовать с инструкцией 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(Путь, выходной поток) , чтобы связать ZipEntry с FileToZip.txt файл внутри ZippedFile.zip 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 , а затем немедленно закрыть ее.

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

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

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

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

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

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 при запуске этих примеров.

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

Источник

Java file in zip archive

Кроме общего функционала для работы с файлами 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».

Источник

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