Назад
Вперед
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).
Рис. 1. Главное окно приложения
Строка Open меню File позволяет выбрать (с помощью
стандартной диалоговой панели класса FileDialog) файл
формата ZIP. Сразу после выбора такого файла на
экране появляется диалоговая панель со списком
имен элементов оглавления выбранного архива ZIP
(рис. 2).
Рис. 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();
Назад Вперед |