Javenue logo

Javenue

Программирование на Java

Информационные технологии

Zip архив в java - компрессия и извлечение файлов

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

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


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

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

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

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

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipUtil {
    public static void main(String[] args) throws Exception {
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream("archive.zip"));

        File file = new File("folder");

        doZip(file, out);

        out.close();
    }

    private static void doZip(File dir, ZipOutputStream out) throws IOException {
        for (File f : dir.listFiles()) {
            if (f.isDirectory())
                doZip(f, out);
            else {
                out.putNextEntry(new ZipEntry(f.getPath()));
                write(new FileInputStream(f), out);
            }
        }
    }

    private static void write(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[1024];
        int len;
        while ((len = in.read(buffer)) >= 0)
            out.write(buffer, 0, len);
        in.close();
    }
}

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


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

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

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

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

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

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

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

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

    import java.io.*;
    import java.util.Enumeration;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;

    public class UnzipUtil {
        public static void main(String[] args) {
            if (args.length != 1) {
                System.out.println("Usage: UnzipUtil [zipfile]");
                return;
            }

            File file = new File(args[0]);
            if (!file.exists() || !file.canRead()) {
                System.out.println("File cannot be read");
                return;
            }

            try {
                ZipFile zip = new ZipFile(args[0]);
                Enumeration entries = zip.entries();

                while (entries.hasMoreElements()) {
                    ZipEntry entry = (ZipEntry) entries.nextElement();
                    System.out.println(entry.getName());

                    if (entry.isDirectory()) {
                        new File(file.getParent(), entry.getName()).mkdirs();
                    } else {
                        write(zip.getInputStream(entry),
                             new BufferedOutputStream(new FileOutputStream(
                                 new File(file.getParent(), entry.getName()))));
                    }
                }

                zip.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private static void write(InputStream in, OutputStream out) throws IOException {
    	    byte[] buffer = new byte[1024];
    	    int len;
    	    while ((len = in.read(buffer)) >= 0)
    		    out.write(buffer, 0, len);
    	    out.close();
    	    in.close();
    	}
    }

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

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


Комментариев: 0

  Выйти

  * для публикации комментариев нужно