Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад]

Библиотека примеров приложений Java

Оглавление
Выбор файлов
Простейший редактор текста
Копирование файлов UNICODE
Сохранение объекта Java в файле
Произвольные классы и файлы
Буферизация потоков
Разбор конфигура-
ционного файла

Работа с консолью
Работа с классом PrintWriter
Разбор строк класса String
Загрузка и просмотр изображений
Потоки в оперативной памяти
Конвейерные потоки
Комбинирование двух потоков
Комбинирование нескольких потоков
Поиск слова в текстовом файле
Произвольный доступ к файлу
Информация о файле
Работа с каталогами
Просмотр содержимого каталога
Просмотр каталога с фильтром
Панель для выбора каталога
Список системных свойств
Сохранение списка системных свойств
Контрольная сумма файла
Копирование, переименование, удаление файлов
Архивы ZIP
Создание архива ZIP
Распаковка архива ZIP

Обход дерева каталогов

Назад Вперед

7.29. Распаковка архива формата ZIP

Показан способ распаковки архива ZIP, путь к которому вводится через консоль. Распакованное дерево каталогов будет расположено в заданном каталоге. Пример демонстрирует использование классов ZipFile и ZipEntry.

Исходный текст примера

Архив проекта для Java WorkShop 2.0

Немного теории

Классы ZipFile и ZipEntry позволяют организовать распаковку существующих архивов формата ZIP.

Как она выполняется?

Прежде всего вы должны создать объект класса ZipFile, передав соответствующему конструктору путь к файлу, например:

ZipFile zf;
zf = new ZipFile(szZipFilePath);

Далее вы получаете перечисление элементов оглавления архива методом entries:

Enumeration en = zf.entries();

Пользуясь этим перечислением, нетрудно организовать извлечение всех элементов оглавления для последующей записи в массив:

while(en.hasMoreElements())
{
  zipEntries.addElement(
   (ZipEntry)en.nextElement());
}

Перебирая в цикле элементы оглавления, вы должны открыть для каждого такого элемента, не являющегося каталогом, входной поток класса InputStream. Такая операция может быть выполнена методом getInputStream, определенным в классе ZipFIle:

ZipEntry ze;
InputStream is = zf.getInputStream(ze);

Далее вы открываете для очередного извлекаемого файла выходной поток класса FileOutputStream и выполняете копирование из потока is в этот выходной поток.

Обрабатывая элементы оглавления архива вы должны учитывать следующие моменты.

Во-первых, имя элемента оглавления может содержать путь к файлу. В качестве разделителя каталогов в этом пути применяется символ "/", который перед созданием выходного каталога следует заменить на символ File.separatorChar. Это нужно сделать, так как на разных платформах применяются разные символы-разделители.

Во-вторых, наряду с элементами, описывающими файлы, в оглавлении могут встречаться элементы с описанием каталогов. Имя таких элементов оканчивается символом "\". Вы можете их или игнорировать, или использовать при создании дерева каталогов.

Описание примера

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

В ходе этого процесса на консоль выводятся пути к создаваемым файлам, их полная длина и длина после сжатия:

Enter full path to Zip-file: 
c:\temp\AppletFrame.zip
Enter path to extract files: 
c:\temp
TEMP\AppletFrame\AppletFrame.tmp.html 307 (178)
TEMP\AppletFrame\AppletFrame.java.bak 904 (339)
TEMP\AppletFrame\AppletFrame.java 1186 (477)
TEMP\AppletFrame\AppletFrame.zip 2913 (2913)
TEMP\AppletFrame\AppletFrame.htm 3683 (1573)
TEMP\AppletFrame\AppletFrame.map 83 (56)
TEMP\AppletFrame\AppletFrame.prj 455 (235)
TEMP\AppletFrame\FrameWindow.class 1219 (727)
TEMP\AppletFrame\AppletFrame.class 998 (562)
Done!

Рассмотрим исходный текст приложения.

Метод main

Получив управление, метод main запрашивает у пользователя два пути - к исходному файлу ZIP и к выходному каталогу. После проверки эти пути сохраняются, соответственно, в переменных szZipFilePath и ExtractPath.

Далее метод main создает объект класса ZipFile, получает перечисление элементов оглавления и сохраняет их в массиве zipEntries:

ZipFile zf; 
Vector zipEntries = new Vector();
       
try
{  
  zf = new ZipFile(szZipFilePath);    
  Enumeration en = zf.entries();
      
  while(en.hasMoreElements())
  {
    zipEntries.addElement(
     (ZipEntry)en.nextElement());
  }
  . . .
}
catch(Exception ex)
{
  System.out.println(ex.toString());
}

Затем элементы массива zipEntries обрабатываются в цикле:

int i;
for (i = 0; i < zipEntries.size(); i++)
{
  ZipEntry ze = 
   (ZipEntry)zipEntries.elementAt(i);
          
  extractFromZip(szZipFilePath, 
    szExtractPath,
    ze.getName(), zf, ze);
}

Мы передаем каждый такой элемент как объект класса ZipEntry методу extractFromZip, определенному в нашем приложении (через последний параметр). Через первый параметр этот метод получает путь к исходному файлу архива ZIP, через второй - путь, куда нужно записать извлеченный файл, через третий - имя файла, а через четвертый - ссылку на объект класса ZipFile.

После завершения цикла метод main закрывает объект zf класса ZipFile и выводит на консоль сообщение о завершении работы:

zf.close();
System.out.println("Done!");

Метод extractFromZip

Прежде всего метод extractFromZip проверяет, не является ли переданный ему элемент описателем каталога:

if(ze.isDirectory())
  return;

Если является, то метод просто возвращает управление для перехода к следующему элементу.

В том случае когда элемент описывает файл, метод extractFromZip выполняет замену разделителей "\" на File.separatorChar, вызывая для этого метод slash2sep, определенный в нашем приложении:

String szDstName = slash2sep(szName);

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

На следующем этапе метод extractFromZip отрезает от этого пути имя файла:

String szEntryDir;
    
if(szDstName.lastIndexOf(File.separator) != -1)
{
  szEntryDir =
    szDstName.substring(0, 
      szDstName.lastIndexOf(File.separator));
}
else     
  szEntryDir = "";

В итоге в переменной szEntryDir будет записан путь к каталогу, куда нужно скоировать извлеченный файл. Если в имени извлекаемого файла нет каталогов и разделителей, файл будет записан в каталог, указанный пользователем с консоли.

Имя файла, извлеченное из элемента оглавления и исправленное (с замененными символами-разделителями), выводится на консоль:

System.out.print(szDstName);

Туда же мы выводим размер исходного и сжатого файла:

long nSize = ze.getSize();
long nCompressedSize = 
  ze.getCompressedSize();
    
System.out.println(" " + nSize + " (" +
  nCompressedSize + ")");

Далее мы создаем дерево каталогов для размещения извлекаемого файла:

try
{
  File newDir = new File(szExtractPath +
    File.separator + szEntryDir);
         
  newDir.mkdirs();
  . . .
}   
catch(Exception ex)
{
  System.out.println(ex.toString());
  System.exit(0);
}

Путь к каталогу образуется путем слияния пути для распаковки, заданного с консоли, и пути, извлеченного из имени файла по элементу оглавления.

Чтобы извлечь файл, мы создаем два потока:

FileOutputStream fos = 
  new FileOutputStream(szExtractPath +
  File.separator + szDstName);
InputStream is = zf.getInputStream(ze);

Поток fos связан с извлекаемым файлом, а поток is - с элементом оглавления архива.

Копирование данных выполняется в цикле:

byte[] buf = new byte[1024];
int nLength;
       
while(true)
{
  try
  {
    nLength = is.read(buf);
  }      
  catch (EOFException ex)
  {
    break;
  }  
         
  if(nLength < 0) 
    break;

  fos.write(buf, 0, nLength);
}

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

is.close();
fos.close(); 

Метод slash2sep

Этот метод предназначен для замены символа-разделителя "/", применяющегося в архивах формата ZIP, на символ File.separatorChar. Такая замена необходима для обеспечения нормальной работы нашего приложения на различных компьютерных платформах.

Замена выполняется простым циклическим просмотром исходной строки, преданной методу в качестве параметра:

static String slash2sep(String src)
{
  int i;
  char[] chDst = new char[src.length()];
  String dst;
    
  for(i = 0; i < src.length(); i++)
  {
    if(src.charAt(i) == '/')
      chDst[i] = File.separatorChar;
    else
      chDst[i] = src.charAt(i);
  }
  dst = new String(chDst);
  return dst;
}

Здесь в цикле на базе исходной строки формируется массив символов chDst, который затем преобразуется в строку класса String и возвращается вызывающему методу.


Назад Вперед

[Назад]