Как заархивировать файл java

Обновлено: 03.07.2024

Для хранения классов языка Java и связанных с ними ресурсов в языке Java используются сжатые архивные jar-файлы.

Для работы с архивами в спецификации Java существуют два пакета – java.util.zip и java.util.jar соответственно для архивов zip и jar. Различие форматов jar и zip заключается только в расширении архива zip. Пакет java.util.jar аналогичен пакету java.util.zip, за исключением реализации конструкторов и метода void putNextEntry(ZipEntry e) класса JarOutputStream. Ниже будет рассмотрен только пакет java.util.jar. Чтобы переделать все примеры на использование zip-архива, достаточно всюду
в коде заме­нить Jar на Zip.

Пакет java.util.jar позволяет считывать, создавать и изменять файлы форматов jar, а также вычислять контрольные суммы входящих потоков данных.

Класс JarEntry (подкласс ZipEntry) используется для предоставления доступа к записям jar-файла. Наиболее важными методами класса являются:

void setMethod(int method) – устанавливает метод сжатия записи;

int getMethod() – возвращает метод сжатия записи;

void setComment(String comment) – устанавливает комментарий записи;

String getComment() – возвращает комментарий записи;

void setSize(long size) – устанавливает размер несжатой записи;

long getSize() – возвращает размер несжатой записи;

long getCompressedSize() – возвращает размер сжатой записи;

У класса JarOutputStream существует возможность записи данных в поток вывода в jar-формате. Он переопределяет метод write() таким образом, чтобы любые данные, записываемые в поток, предварительно сжимались. Основными методами данного класса являются:

void setLevel(int level) – устанавливает уровень сжатия. Чем больше уровень сжатия, тем медленней происходит работа с таким файлом;

void putNextEntry(ZipEntry e) – записывает в поток новую jar-запись. Этот метод переписывает данные из экземпляра JarEntry в поток вывода;

void closeEntry() – завершает запись в поток jar-записи и заносит дополнительную информацию о ней в поток вывода;

void write(byte b[], int off, int len) – записывает данные из буфера b начиная с позиции off длиной len в поток вывода;

void finish() – завершает запись данных jar-файла в поток вывода без закрытия потока;

void close() – закрывает поток записи.

package chapt09;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.util.jar.JarEntry;

import java.util.jar.JarOutputStream;

import java.util.zip.Deflater;

public class PackJar

public static void pack(String[] filesToJar,

String jarFileName, byte[] buffer)

new JarOutputStream(

new FileOutputStream(jarFileName));

// метод сжатия

jos.setLevel(Deflater.DEFAULT_COMPRESSION);

for (int i = 0; i < filesToJar.length; i++)

System.out.println(i);

jos.putNextEntry(new JarEntry(filesToJar[i]));

new FileInputStream(filesToJar[i]);

while ((len = in.read(buffer)) > 0)

jos.write(buffer, 0, len);

> catch (IllegalArgumentException e)

System.err.println("Некорректный аргумент");

> catch (FileNotFoundException e)

System.err.println("Файл не найден");

> catch (IOException e)

System.err.println("Ошибка доступа");

public static void main(String[] args)

System.out.println("Создание jar-архива");

// массив файлов для сжатия

String[] filesToJar = new String[2];

byte[] buffer = new byte[1024];

// имя полученного архива

String jarFileName = "example.jar";

pack(filesToJar, jarFileName, buffer);

Класс JarInputStream читает данные в jar-формате из потока ввода. Он переопределяет метод read() таким образом, чтобы любые данные, считываемые из потока, предварительно распаковывались.

package chapt09;

import java.io.*;

import java.util.Enumeration;

import java.util.jar.JarEntry;

import java.util.jar.JarFile;

public class UnPackJar

private File destFile;

// размер буфера для распаковки

public final int BUFFER = 2048;

public void unpack(String destinationDirectory,

File sourceJarFile = new File(nameJar);

new File(destinationDirectory);

// открытие zip-архива для чтения

JarFile jFile = new JarFile(sourceJarFile);

Enumeration jarFileEntries = jFile.entries();

while (jarFileEntries.hasMoreElements())

// извлечение текущей записи из архива

Думаю, многие встречались с необходимостью создавать zip-архивы в java программах. Распаковка архивов из программного кода встречается куда реже, но все же встречается.

Давайте рассмотрим API для работы с архивами. Необходимые нам классы находятся в пакете java.util.zip.

Создание архива

Чтобы создать новый архив необходимо воспользоваться классом ZipOutputStream. Вот список методов, которые могут понадобиться:

  • setLevel - установка уровня компрессии от 0 до 9, где 9 - максимальная компрессия;
  • putNextEntry - вызывается перед записью нового объекта в архив, с указанием имени объекта;
  • closeEntry - вызываем после записи объекта. putNextEntry автоматически вызывает метод closeEntry.
  • close - закрываем поток.

Небольшой пример - создадим архив с названием archive.zip, в котором будут находиться сжатые файлы из директории folder. В этом примере пустые директории будут игнорироваться. Уровень компрессии явно не задан, поєтому будет использоваться значение по-умолчанию.

Обратите внимание, что при создании ZipEntry мы использовали относительный путь, а не просто имя файла. Это сделано для того, чтобы при архивации сохранились все дерево директорий, ведущих к файлу. В случае использования f.getName() в архиве просто будет плоский список файлов без информации о директориях.

Извлечение файлов из архива

Давайте теперь напишем небольшую утилиту на Java для извлечения фалов из zip-архива. Необходимые нам классы из пакета java.util.zip - это ZipFile и ZipEntry.

Для начала проверим, что на вход программы пользователь подал путь к архиву. Далее убедимся, что файл архива существует и может быть прочитан. Если все нормально, переходим непосредственно к процессу разархивации.

ZipFile как понятно из названия класса представляет собой файл архива. Одним из самых важных методов класса есть метод entries(). Метод возвращает перечисление объектов архива.

Итерируя по перечислению мы создаем необходимые пустые директории, если такие имеются, а так же пишем объекты на файловую систему, опять же с сохранением всех директорий.

У объекта ZipEntry кроме getName есть еще такие употребимые методы:

  • getSize - размер файла в несжатом виде;
  • getCompressedSize - размер, занимаемый файлом в архиве;
  • getTime - время последней модификации объекта.

Ну а вот и исходник утилиты:

write() - вспомогательный метод, который пишет из одного потока в другой. Кстати, новичкам стоит запомнить, как из InputStream переписать информацию в OutputStream - такой вопрос периодически задают джуниорам на собеседовании.

Если еще остались вопросы - задавайте их в комментариях, постараюсь помочь.

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

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

Содержание

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Git Essentials

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

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

Вывод

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

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

В мобильной разработке бывает потребность сделать приложение для работы без интернета. Например, словарь или справочник, который будет использоваться в суровых полевых условиях. Тогда для работы приложения нужно единожды выкачать архив с данными и сохранить его у себя. Сделать это можно посредством запроса в сеть, но так же можно зашить архив с данными внутрь приложения.

Согласно требованиям Google Play, apk-файл приложения должен быть не более 50 МБ, так же можно прикрепить два файла дополнения .obb по 2 гигабайта. Механизм простой, но сложный при эксплуатации, поэтому лучше всего уложиться в 50 МБ и возрадоваться. И в этом нам помогут целых два архивных формата Zip и 7z.

Давайте рассмотрим их работу на примере уже готового тестового приложения ZipExample.

Для тестов была создана sqlite база данных test_data.db. Она содержит 2 таблицы android_metadata — по традиции и my_test_data с миллионом строчек:


Размер полученного файла составляет 198 МБ.

Сделаем два архива test_data.zip (10.1 МБ) и test_data.7z (3.05 МБ).

Как очевидно, файлы БД sqlite очень хорошо сжимаются. По опыту могу сказать, что чем проще структура базы, тем лучше сжимается. Оба этих файла располагаются в папке assets и в процессе работы будут разархивированы.


Внешний вид программы представляет собой окно с текстом и двумя кнопками:


Вот метод распаковки zip архива:

Распаковывающим классом тут является ZipInputStream он входит в пакет java.util.zip, а тот в свою очередь в стандартную Android SDK и поэтому работает «из коробки» т.е. ничего отдельно закачивать не надо.

Вот метод распаковки 7z архива:

Вот экран работающего приложения:


Размер отладочной версии приложения получился 6,8 МБ.
А вот его размер в устройстве после распаковки:


Внимание вопрос кто в черном ящике что в кеше?

В заключении хочу сказать, что распаковка архивов занимает продолжительное время и поэтому нельзя его делать в основном (UI) потоке. Это приведет к подвисанию интерфейса. Во избежание этого можно задействовать AsyncTask , а лучше фоновый сервис т.к. пользователь может не дождаться распаковки и выйти, а вы получите ошибку (правда если не поставите костылей в методе onPostExecute).

Читайте также: