Архивирование директорий, zip

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

При работе с архивами еще в 1999 г. был зарегистрирован баг №4244499, связанный с наименованием файлов, использующие не латинские символы. То есть, наименования файлов с кирилицей искажались. И только в версии JDK 7 этот баг был исправлен - в ZIP API добавили новый конструктор, в котором можно явно указывать кодировку: ZipOutputStream (OutputStream out, Charset charset) К тому же, по-умолчанию используется стандартная UTF-8 кодировка. Таким образом, дополнительных библиотек использовать не нужно, можно сразу делать zip-архив.

К сожалению, мне не удалось создать нормальный архив с русскоязычными символами в наименованиях файлов и директорий. Поэтому в примерах я использовал библиотеку apache-ant-zip.jar, которая успешно решает вопросы архивации файлов, в названии которых используется кириллица.

В качестве примера будем использовать трехуровневую директорию следующей структуры :

Архивирование директории

Для правильной кодировки наименований русскоязычных файлов в примере используется метод setEncoding класса ZipOutputStream.

private void Zip(String source_dir, String zip_file) throws Exception
{
    // Cоздание объекта ZipOutputStream из FileOutputStream
    FileOutputStream fout = new FileOutputStream(zip_file);
    ZipOutputStream zout = new ZipOutputStream(fout);
    // Определение кодировки
    zout.setEncoding("CP866");
        	
    // Создание объекта File object архивируемой директории
    File fileSource = new File(source_dir);
               
    addDirectory(zout, fileSource);
               
    // Закрываем ZipOutputStream
    zout.close();
               
    System.out.println("Zip файл создан!");
}
private void addDirectory(ZipOutputStream zout, File fileSource) 
                                                throws Exception
{
    File[] files = fileSource.listFiles();
    System.out.println("Добавление директории <" + fileSource.getName() + ">");
    for(int i = 0; i < files.length; i++) {
        // Если file является директорией, то рекурсивно вызываем 
        // метод addDirectory
        if(files[i].isDirectory()) {
            addDirectory(zout, files[i]);
            continue;
        }
        System.out.println("Добавление файла <" + files[i].getName() + ">");

        FileInputStream fis = new FileInputStream(files[i]);
       		
        zout.putNextEntry(new ZipEntry(files[i].getPath()));

        byte[] buffer = new byte[4048];
        int length;
        while((length = fis.read(buffer)) > 0)
            zout.write(buffer, 0, length);
	    // Закрываем ZipOutputStream и InputStream
        zout.closeEntry();
        fis.close();
    }
}

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

Результат работы программы можно увидеть в консоли :

Добавление директории <Тестовая папка>
Добавление файла <Документ Word 1.docx>
Добавление файла <Лист Excel 1.xlsx>
Добавление файла <Текстовый документ 1.txt>
Добавление директории <Уровень 2>
Добавление файла <Документ Word 2.docx>
Добавление файла <Лист Excel 2.xlsx>
Добавление файла <Текстовый документ 2.txt>
Добавление директории <Уровень 3.1>
Добавление файла <Документ Word 3.1.docx>
Добавление файла <Лист Excel 3.1.xlsx>
Добавление файла <Текстовый документ 3.1.txt>
Добавление директории <Уровень 3.2>
Добавление файла <Документ Word 3.2.docx>
Добавление файла <Лист Excel 3.2.xlsx>
Добавление файла <Текстовый документ 3.2.txt>
Zip файл создан!
 

Разархивирование директории

В примере разархивирования используется файл, созданный в предыдущем примере архивирования директории.

Метод Unzip в качестве входного параметра принимает архивный файл. ZipFile позволяет получить все "вхождения" директорий и файлов в архив. После этого в цикле извлекается содержимое архива.

private final static String zipDir  = "Тестовая папка";
private final static String zipFile = "Тестовая папка.zip";

private final String SLASH_BACK     = "/";
private final String CHARSET_CP866  = "CP866";

private void createDir(final String dir)
{
    File file = new File(dir);
    if (!file.exists())
        file.mkdirs();
}
private void createFolder(final String dirName)
{
    if (dirName.endsWith(SLASH_BACK))
        createDir(dirName.substring(0, dirName.length() - 1));
}
private void checkFolder(final String file_path)
{
    if (!file_path.endsWith(SLASH_BACK) && file_path.contains(SLASH_BACK)) {
        String dir = file_path.substring(0, file_path.lastIndexOf(SLASH_BACK));
        createDir(dir);
    }
}
private void Unzip(final String zipDir) throws Exception
{
    ZipFile zipFile = new ZipFile(zipDir, CHARSET_CP866);
    Enumeration<?> entries = zipFile.getEntries();
    while (entries.hasMoreElements()) {
        ZipEntry entry = (ZipEntry) entries.nextElement();
        String entryName = entry.getName();
        if (entryName.endsWith(SLASH_BACK)) {
            System.out.println("Создание директории <" + entryName + ">");
            createFolder (entryName);
            continue;
        } else
            checkFolder(entryName);
        System.out.println("Чтение файла <" + entryName + ">");
        InputStream  fis = (InputStream) zipFile.getInputStream(entry);
       		
        FileOutputStream fos = new FileOutputStream(entryName);
        byte[] buffer = new byte[fis.available()];
        // Считываем буфер
        fis.read(buffer, 0, buffer.length);
        // Записываем из буфера в файл
        fos.write(buffer, 0, buffer.length);
        fis.close();
        fos.close();
    }
    zipFile.close() ;      
    System.out.println("Zip файл разархивирован!");
}

Результат работы программы можно увидеть в консоли :


Чтение файла <Тестовая папка/Уровень 1/Уровень 2.1/Документ Word 2.1.docx>
Создание директории <Тестовая папка/Уровень 1/Уровень 2.1/>
Чтение файла <Тестовая папка/Уровень 1/Уровень 2.2/Документ Word 2.2.docx>
Чтение файла <Тестовая папка/Лист Excel 1.xlsx>
Чтение файла <Тестовая папка/Уровень 1/Уровень 2.1/Лист Excel 2.1.xlsx>
Чтение файла <Тестовая папка/Документ Word 1.docx>
Чтение файла <Тестовая папка/Уровень 1/Уровень 2.2/Текстовый документ 2.2.txt>
Чтение файла <Тестовая папка/Уровень 1/Уровень 2.2/Лист Excel 2.2.xlsx>
Чтение файла <Тестовая папка/Уровень 1/Документ Word 2.docx>
Создание директории <Тестовая папка/>
Создание директории <Тестовая папка/Уровень 1/>
Чтение файла <Тестовая папка/Уровень 1/Лист Excel 2.xlsx>
Чтение файла <Тестовая папка/Текстовый документ 1.txt>
Чтение файла <Тестовая папка/Уровень 1/Текстовый документ 2.txt>
Чтение файла <Тестовая папка/Уровень 1/Уровень 2.1/Текстовый документ 2.1.txt>
Создание директории <Тестовая папка/Уровень 1/Уровень 2.2/>
Zip файл разархивирован!
 

Скачать пример

Исходный код рассмотренного примера архивирирования многоуровневой директории и разархивирование файла в виде проекта Eclipse можно скачать здесь (56.6 Мб). Библиотека apache-ant-zip.jar, используемая в примере, включена в проект.

  Рейтинг@Mail.ru