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

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

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

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

Назад Вперед

7.27. Работа с архивами формата ZIP

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

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

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

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

Среди различных библиотек классов Java есть одна очень интересная - с названием java.util.zip. Она позволяет работать с библиотеками файлов широко распространенных форматов ZIP и GZIP.

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

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

Чтобы получить доступ к таким файлам, вы должны прежде всего создать объект класса ZipFile, пользуясь одним из конструкторов этого класса. Первый конструктор позволяет открыть ZIP-файл через объект класса File, а второй - через полный путь к имени файла:

try
{  
  zf = new ZipFile(szZipFilePath);
  . . .
}
catch(Exception ex)
{
  System.out.println(ex.toString());
}

В процессе создания объекта класса ZipFile могут возникать как обычные для файлов исключения IOException, так и исключения класса ZipException, специфические для класса ZipFile.

Класс ZipFile

В классе ZipFile определены несколько методов:

Метод Описание
entries Возвращает перечисление объектов, хранящихся в архивной библиотеке формата ZIP
getName Возвращает имя ZIP-файла
getInputStream Возвращает ссылку на входной поток для чтения объектов их архива
getEntry Возвращает объект, хранящийся в библиотеке, по его имени
close Закрывает ZIP-файл

Методы entries и getEntry позволяют получить элементы, хранящиеся в архиве, как объекты класса ZipEntry. Ниже мы получаем перечисление таких объектов:

Enumeration en = zf.entries();

Чтобы извлечь элементы перечисления и записать их в массив zipEntries класса Vector, мы можем создать цикл:

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

Класс ZipEntry

В классе ZipEntry определены две константы, конструктор и несколько методов.

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

public static final int STORED;
public static final int DEFLATED;

Если файл сохранен в архиве без компрессии, для него используется метод ZipEntry.STORED, а если с компрессией - метод ZipEntry.DEFLATED.

Конструктор класса ZipEntry позволяет создать новый элемент оглавления архива с заданным именем:

public ZipEntry(String name);

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

Рассмотрим эти методы.

setCrc
public void setCrc(long crc);

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

getCrc
public long getCrc();

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

setMethod
public void setMethod(int method);

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

getMethod
public int getMethod();

Метод getMethod позволяет знать способ компрессии, который был использован для файла, соответствующего данному элементу архива. Он может возвращать значения ZipEntry.DEFLATED или ZipEntry.STORED.

setExtra
public void setExtra(byte extra[]);

С помощью этого метода можно записать в элемент оглавления архива произвольную дополнительную информацию.

getExtra
public byte[] getExtra();

Метод getExtra возвращает дополнительную информацию, записанную в элементе оглавления архива.

setComment
public void setComment(String comment);

Запись в элемент оглавления архива дополнительной текстовой строки комментария.

getComment
public String getComment();

Чтение строки комментария.

getCompressedSize
public long getCompressedSize();

Метод getCompressedSize возвращает размер сжатого файла или значение -1, если этот размер неизвестен.

isDirectory
public boolean isDirectory();

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

getName
public String getName();

Этот метод позволяет узнать имя, соответствующее данному элементу оглавления архива. Изменить имя нельзя - оно передается конструктору класса ZipEntry при создании нового элемента оглавления.

setTime
public void setTime(long time);

Установка времени модификации файла (в количестве миллисекунд, прошедших с начала эпохи).

getTime
public long getTime();

Определение времени модификации файла.

setSize
public void setSize(long size);

Установка размера несжатого файла.

getSize
public long getSize();

Определение размера несжатого файла.

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

Наш пример автономного приложения ZipFileView создает одно главное окно класса Frame, в котором предусмотрено меню (рис. 1).

pic1.gif (3923 bytes)

Рис. 1. Главное окно приложения

Строка Open меню File позволяет выбрать (с помощью стандартной диалоговой панели класса FileDialog) файл формата ZIP. Сразу после выбора такого файла на экране появляется диалоговая панель со списком имен элементов оглавления выбранного архива ZIP (рис. 2).

pic2.gif (4682 bytes)

Рис. 2. Список элементов оглавления выбранного архива

Если выделить в этом списке любую строку, в главном окне приложения появится описание содержимого соответствующего элемента оглавления (рис. 1).

С помощью кнопки Extract вы сможете извлечь выделенный файл из архива и сохранить его на диске. Для выбора пути и имени сохраняемого файла наше приложение отобразит на экране стандартную диалоговую панель класса FileDialog.

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

Прежде всего обратите внимание на классы, подключаемые оператором import:

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.text.*;

Классы java.util.zip.* нужны для работы с файлами ZIP, а класс java.text.* - для форматирования строки даты изменения файлов, записанных в архиве.

Когда пользователь выбирает строку Open из меню File, метод actionPerformed, определенный в классе FrameWindow, отображает на экране стандартную диалоговую панель для выбора ZIP-файла:

public void actionPerformed(ActionEvent e)
{
  String szSelected = null;
    
  if(e.getSource().equals(miOpen))
  {
    fdlg = new FileDialog(this, "Open ZIP-file",
      FileDialog.LOAD);
    fdlg.setFile("*.zip");
    fdlg.show();
      
    if(fdlg.getDirectory() == null ||
      fdlg.getFile() == null)
       return;
      
    String szPath = fdlg.getDirectory() +
      fdlg.getFile();
    . . .
}

Путь к выбранному файлу записывается в переменную szPath. Он затем передается конструктору класса ZipFileDialog, определенному в нашем приложении для отображения списка элементов оглавления ZIP-файла:

ZipFileDialog d = new ZipFileDialog(
  "ZIP File: ", szPath, this);
d.show();

Класс ZipFileDialog

Класс ZipFileDialog создан на базе класса Dialog и реализует ряд интерфейсов:

class ZipFileDialog extends Dialog
  implements ActionListener, WindowListener,
    ItemListener
{
  . . .
}

В этом классе определено несколько полей.

В поле zf класса ZipFile хранится ссылка на объект, созданная для выбранного пользователем ZIP-файла:

ZipFile zf;

Массив zipEntries класса Vector предназначен для хранения извлеченных элементов оглавления ZIP-файла:

Vector zipEntries = new Vector();

Конструктор класса ZipFileDialog создает все необходимые компоненты и добавляют их в окно панели с применением режима размещения компонент GridBagLayout. Перед завершением своей работы он вызывает метод updateList:

updateList();

Этот метод заполняет список, расположенный в окне панели, именами элементов оглавления архива.

Метод updateList класса ZipFileDialog

Рассмотрим исходный текст метода updateList, заполняющий список именами элементов оглавления архива.

Первым делом этот метод очищает список, удаляя из него все элементы:

l.removeAll();

Далее метод updateList выполняет все операции в блоке try-catch, что необходимо для обработки исключений.

Первым делом мы создаем объект класса ZipFile и с помощью метода entries получаем перечисление элементов оглавления архива:

try
{  
  zf = new ZipFile(szZipFilePath);    
  Enumeration en = zf.entries();
  . . .
}

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

setTitle("ZIP - " + szZipFilePath);

Пользуясь полученным перечислением мы заполняем массив zipEntries, записывая в него объекты класса ZipEntry:

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

Далее перебирая элементы массива zipEntries в цикле мы извлекаем имена элементов и добавляем их в список, расположенный в окне диалоговой панели:

int i = 0;
for (i = 0; i < zipEntries.size(); i++)
{
  ZipEntry ze = 
   (ZipEntry)zipEntries.elementAt(i);
  l.add(ze.getName());
}

Метод itemStateChanged класса ZipFileDialog

Когда пользователь выделяет в списке строку имени элемента оглавления массива, управление передается методу itemStateChanged, реализованному как часть интерфейса ItemListener:

public void itemStateChanged(ItemEvent e)
{
  getZipFileEntryInfo();
}

Наша реализация этого метода вызывает метод getZipFileEntryInfo, извлекающий информацию из элемента оглавления архива и отображающий ее в главном окне приложения.

Метод getZipFileEntryInfo класса ZipFileDialog

Получив управление, метод getZipFileEntryInfo определяет номер элемента, выделенного пользователем в списке:

int nSelected = l.getSelectedIndex();
if(nSelected < 0)
  return;

Далее из массива zipEntries извлекается соответствующий элемент класса ZipEntry:

ZipEntry ze = 
  (ZipEntry)zipEntries.elementAt(nSelected);

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

((FrameWindow)par).ta.selectAll();
 ((FrameWindow)par).ta.replaceRange(
 "", 0, 
 ((FrameWindow)par).ta.getSelectionEnd());

Затем мы формируем новый текст в переменной szItemInfo, записывая туда значения атрибутов, извлеченные из элемента оглавления соответствующими методами класса ZipEntry:

szItemInfo = "Entry name: " + ze.getName();
szItemInfo += "\nComment: " + ze.getComment();
szItemInfo += "\nCompressed size: " + 
  ze.getCompressedSize();
szItemInfo += "\nSize: " + ze.getSize();
szItemInfo += "\nCRC-32: " + ze.getCrc();
szItemInfo += "\nExtra data: " + ze.getExtra();

szItemInfo += "\nCRC-32: " + ze.getCrc();
szItemInfo += "\nDate: " + 
  DateFormat.getDateTimeInstance(
    DateFormat.FULL, DateFormat.FULL).format(
    new Date(ze.getTime()));
    
if(ze.getMethod() ==  ZipEntry.DEFLATED)
  szItemInfo += "\nMethod: DEFLATED";
      
else if(ze.getMethod() ==  ZipEntry.STORED)
  szItemInfo += "\nMethod: STORED";
    
if(ze.isDirectory())
  szItemInfo += "\n- DIRECTORY -";
else  
  szItemInfo += "\n- FILE -";

Затем полученная строка добавляется в редактор главного окна приложения:

((FrameWindow)par).ta.append(szItemInfo);

Метод actionPerformed класса ZipFileDialog

Когда пользователь нажимает кнопку Extract, расположенную в нижней части диалоговой панели со списком, метод actionPerformed извлекает из списка имя выделенного элемента архива и передает его методу saveZipFile:

public void actionPerformed(ActionEvent e)
{
  if(e.getSource().equals(bExtract))
  {
    String s = l.getSelectedItem();
    if(s != null)
      saveZipFile(s);
  }
  . . .  
}

Метод saveZipFile класса ZipFileDialog

В задачу метода saveZipFile входит извлечение из архива указанного файла и сохранение его на диске.

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

s = s.substring(s.lastIndexOf("/") + 1);

Это имя затем используется при инициализации панели выбора выходного класса:

FileDialog fdlg;
fdlg = new FileDialog(par, "Save file as...", 
  FileDialog.SAVE);
fdlg.setFile(s);
fdlg.show();

if(fdlg.getDirectory() == null ||
  fdlg.getFile() == null)
  return;
      
String szPath = fdlg.getDirectory() +
  fdlg.getFile();

Таким образом, пользователю предлагается сохранить файл под тем именем, под которым он хранился в архиве.

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

На следующем этапе метод создает выходной поток, связанный с файлом:

int nSelected;
try
{
  FileOutputStream fos = 
    new FileOutputStream(szPath);
  . . .
}
catch(Exception ex)
{
  System.out.println(ex.toString());
}

Узнав номер строки, выделенной пользователем в списке имен файлов, мы извлекаем из массива zipEntries соответствующий элемент оглавления класса ZipEntry:

nSelected = l.getSelectedIndex();
if(nSelected < 0)
  return;
       
ZipEntry ze = 
  (ZipEntry)zipEntries.elementAt(nSelected);

Далее нам необходимо скопировать данные из архива в выходной файл fos.

Чтобы это сделать, мы получаем ссылку на входной поток, связанный с объектом ZipFile:

InputStream is = zf.getInputStream(ze);

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

int nLength;
byte[] buf = new byte[8000];
while(true)
{
  nLength = is.read(buf);
  if(nLength < 0) 
    break;
  fos.write(buf, 0, nLength);
}
is.close();
fos.close();

Назад Вперед

[Назад]