Java работа с архивом

Архивирование и разархивирование в Java

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

Эти основные библиотеки являются частью пакетаjava.util.zip, в котором мы можем найти все утилиты, связанные с архивированием и распаковкой.

2. Заархивировать файл

Давайте сначала рассмотрим простую операцию — заархивировать один файл.

В нашем примере мы заархивируем файл с именемtest1.txt в архив с именемcompressed.zip. __

Конечно, сначала мы получим доступ к файлу с диска — давайте посмотрим:

public class ZipFile < public static void main(String[] args) throws IOException < String sourceFile = "test1.txt"; FileOutputStream fos = new FileOutputStream("compressed.zip"); ZipOutputStream zipOut = new ZipOutputStream(fos); File fileToZip = new File(sourceFile); FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) < zipOut.write(bytes, 0, length); >zipOut.close(); fis.close(); fos.close(); > >

3. Заархивировать несколько файлов

Затем давайте посмотрим, как заархивировать несколько файлов в один ZIP-файл. Мы сжимаемtest1.txt иtest2.txt вmultiCompressed.zip:

public class ZipMultipleFiles < public static void main(String[] args) throws IOException < ListsrcFiles = Arrays.asList("test1.txt", "test2.txt"); FileOutputStream fos = new FileOutputStream("multiCompressed.zip"); ZipOutputStream zipOut = new ZipOutputStream(fos); for (String srcFile : srcFiles) < File fileToZip = new File(srcFile); FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) < zipOut.write(bytes, 0, length); >fis.close(); > zipOut.close(); fos.close(); > >

4. Заархивировать каталог

Теперь давайте обсудим, как заархивировать весь каталог. Мы каталогизируемzipTest вdirCompressed.zip:

public class ZipDirectory < public static void main(String[] args) throws IOException < String sourceFile = "zipTest"; FileOutputStream fos = new FileOutputStream("dirCompressed.zip"); ZipOutputStream zipOut = new ZipOutputStream(fos); File fileToZip = new File(sourceFile); zipFile(fileToZip, fileToZip.getName(), zipOut); zipOut.close(); fos.close(); >private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException < if (fileToZip.isHidden()) < return; >if (fileToZip.isDirectory()) < if (fileName.endsWith("/")) < zipOut.putNextEntry(new ZipEntry(fileName)); zipOut.closeEntry(); >else < zipOut.putNextEntry(new ZipEntry(fileName + "/")); zipOut.closeEntry(); >File[] children = fileToZip.listFiles(); for (File childFile : children) < zipFile(childFile, fileName + "/" + childFile.getName(), zipOut); >return; > FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileName); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while ((length = fis.read(bytes)) >= 0) < zipOut.write(bytes, 0, length); >fis.close(); > >
  • Чтобы сжать подкаталоги, мы рекурсивно перебираем их.
  • Каждый раз, когда мы находим каталог, мы добавляем его имя к имени потомковZipEntry, чтобы сохранить иерархию.
  • Мы также создаем запись каталога для каждого пустого каталога

5. Распаковать архив

Теперь распакуем архив и извлечем его содержимое.

В этом примере мы распакуемcompressed.zip в новую папку с именемunzipTest.

public class UnzipFile < public static void main(String[] args) throws IOException < String fileZip = "src/main/resources/unzipTest/compressed.zip"; File destDir = new File("src/main/resources/unzipTest"); byte[] buffer = new byte[1024]; ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip)); ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) < File newFile = newFile(destDir, zipEntry); FileOutputStream fos = new FileOutputStream(newFile); int len; while ((len = zis.read(buffer)) >0) < fos.write(buffer, 0, len); >fos.close(); zipEntry = zis.getNextEntry(); > zis.closeEntry(); zis.close(); > public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException < File destFile = new File(destinationDir, zipEntry.getName()); String destDirPath = destinationDir.getCanonicalPath(); String destFilePath = destFile.getCanonicalPath(); if (!destFilePath.startsWith(destDirPath + File.separator)) < throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); >return destFile; > >

Другой ключевой момент можно увидеть в методеnewFile(). Этот метод защищает от записи файлов в файловую систему за пределами целевой папки. Эта уязвимость называется Zip Slip, и вы можетеread more about it here.

Читайте также:  При нажатии кнопки открывается форма html

6. Заключение

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

Реализацию этих примеров можно найти вover on GitHub — это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.

Источник

Package java.util.zip

Provides classes for reading and writing the standard ZIP and GZIP file formats. Also includes classes for compressing and decompressing data using the DEFLATE compression algorithm, which is used by the ZIP and GZIP file formats. Additionally, there are utility classes for computing the CRC-32, CRC-32C and Adler-32 checksums of arbitrary input streams.

Package Specification

  • Info-ZIP Application Note 970311 — a detailed description of the Info-ZIP format upon which the java.util.zip classes are based.
  • An implementation may optionally support the ZIP64(tm) format extensions defined by thePKWARE ZIP File Format Specification. The ZIP64(tm) format extensions are used to overcome the size limitations of the original ZIP format.
  • APPENDIX D ofPKWARE ZIP File Format Specification — Language Encoding Flag to encode ZIP entry filename and comment fields using UTF-8.
  • ZLIB Compressed Data Format Specification version 3.3(pdf) (RFC 1950)
  • DEFLATE Compressed Data Format Specification version 1.3(pdf) (RFC 1951)
  • GZIP file format specification version 4.3(pdf) (RFC 1952)
  • CRC-32 checksum is described in RFC 1952 (above)
  • CRC-32C checksum is described in Internet Small Computer Systems Interface (iSCSI)(pdf) (RFC 3720)
  • Adler-32 checksum is described in RFC 1950 (above)

This class provides support for general purpose compression using the popular ZLIB compression library.

This class implements an output stream filter for compressing data in the «deflate» compression format.

This class provides support for general purpose decompression using the popular ZLIB compression library.

Implements an output stream filter for uncompressing data stored in the «deflate» compression format.

Report a bug or suggest an enhancement
For further API reference and developer documentation see the Java SE Documentation, which contains more detailed, developer-targeted descriptions with conceptual overviews, definitions of terms, workarounds, and working code examples.
Java is a trademark or registered trademark of Oracle and/or its affiliates in the US and other countries.
Copyright © 1993, 2023, Oracle and/or its affiliates, 500 Oracle Parkway, Redwood Shores, CA 94065 USA.
All rights reserved. Use is subject to license terms and the documentation redistribution policy.

Источник

Java работа с архивом

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

Читайте также:  Html form post input types

ZipOutputStream. Запись архивов

Для создания архива используется класс ZipOutputStream. Для создания объекта ZipOutputStream в его конструктор передается поток вывода:

ZipOutputStream(OutputStream out)

Для записи файлов в архив для каждого файла создается объект ZipEntry , в конструктор которого передается имя архивируемого файла. А чтобы добавить каждый объект ZipEntry в архив, применяется метод putNextEntry() .

import java.io.*; import java.util.zip.*; public class Program < public static void main(String[] args) < String filename = "notes.txt"; try(ZipOutputStream zout = new ZipOutputStream(new FileOutputStream("output.zip")); FileInputStream fis= new FileInputStream(filename);) < ZipEntry entry1=new ZipEntry("notes.txt"); zout.putNextEntry(entry1); // считываем содержимое файла в массив byte byte[] buffer = new byte[fis.available()]; fis.read(buffer); // добавляем содержимое к архиву zout.write(buffer); // закрываем текущую запись для новой записи zout.closeEntry(); >catch(Exception ex) < System.out.println(ex.getMessage()); >> >

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

Чтение архивов. ZipInputStream

Для чтения архивов применяется класс ZipInputStream . В конструкторе он принимает поток, указывающий на zip-архив:

ZipInputStream(InputStream in)

Для считывания файлов из архива ZipInputStream использует метод getNextEntry() , который возвращает объект ZipEntry . Объект ZipEntry представляет отдельную запись в zip-архиве. Например, считаем какой-нибудь архив:

import java.io.*; import java.util.zip.*; public class Program < public static void main(String[] args) < try(ZipInputStream zin = new ZipInputStream(new FileInputStream("output.zip"))) < ZipEntry entry; String name; while((entry=zin.getNextEntry())!=null)< name = entry.getName(); // получим название файла System.out.printf("File name: %s \n", name); // распаковка FileOutputStream fout = new FileOutputStream("new" + name); for (int c = zin.read(); c != -1; c = zin.read()) < fout.write(c); >fout.flush(); zin.closeEntry(); fout.close(); > > catch(Exception ex) < System.out.println(ex.getMessage()); >> >

ZipInputStream в конструкторе получает ссылку на поток ввода. И затем в цикле выводятся все файлы и их размер в байтах, которые находятся в данном архиве.

Затем данные извлекаются из архива и сохраняются в новые файлы, которые находятся в той же папке и которые начинаются с «new».

Источник

Работа с архивами Zip и 7z

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

Согласно требованиям Google Play, apk-файл приложения должен быть не более 50 МБ, так же можно прикрепить два файла дополнения .obb по 2 гигабайта. Механизм простой, но сложный при эксплуатации, поэтому лучше всего уложиться в 50 МБ и возрадоваться. И в этом нам помогут целых два архивных формата Zip и 7z.

Давайте рассмотрим их работу на примере уже готового тестового приложения ZipExample.

Для тестов была создана sqlite база данных test_data.db. Она содержит 2 таблицы android_metadata — по традиции и my_test_data с миллионом строчек:

Читайте также:  Java org json android

Размер полученного файла составляет 198 МБ.

Сделаем два архива test_data.zip (10.1 МБ) и test_data.7z (3.05 МБ).

Как очевидно, файлы БД sqlite очень хорошо сжимаются. По опыту могу сказать, что чем проще структура базы, тем лучше сжимается. Оба этих файла располагаются в папке assets и в процессе работы будут разархивированы.

Внешний вид программы представляет собой окно с текстом и двумя кнопками:

Вот метод распаковки zip архива:

 public void onUnzipZip(View v) throws IOException < SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSS"); String currentDateandTime = sdf.format(new Date()); String log = mTVLog.getText().toString() + "\nStart unzip zip" + currentDateandTime; mTVLog.setText(log); InputStream is = getAssets().open("test_data.zip"); File db_path = getDatabasePath("zip.db"); if (!db_path.exists()) db_path.getParentFile().mkdirs(); OutputStream os = new FileOutputStream(db_path); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is)); ZipEntry ze; while ((ze = zis.getNextEntry()) != null) < byte[] buffer = new byte[1024]; int count; while ((count = zis.read(buffer)) >-1) < os.write(buffer, 0, count); >os.close(); zis.closeEntry(); > zis.close(); is.close(); currentDateandTime = sdf.format(new Date()); log = mTVLog.getText().toString() + "\nEnd unzip zip" + currentDateandTime; mTVLog.setText(log); > 

Распаковывающим классом тут является ZipInputStream он входит в пакет java.util.zip, а тот в свою очередь в стандартную Android SDK и поэтому работает «из коробки» т.е. ничего отдельно закачивать не надо.

Вот метод распаковки 7z архива:

public void onUnzip7Zip(View v) throws IOException < SimpleDateFormat sdf = new SimpleDateFormat(" HH:mm:ss.SSS"); String currentDateandTime = sdf.format(new Date()); String log = mTVLog.getText().toString() + "\nStart unzip 7zip" + currentDateandTime; mTVLog.setText(log); File db_path = getDatabasePath("7zip.db"); if (!db_path.exists()) db_path.getParentFile().mkdirs(); SevenZFile sevenZFile = new SevenZFile(getAssetFile(this, "test_data.7z", "tmp")); SevenZArchiveEntry entry = sevenZFile.getNextEntry(); OutputStream os = new FileOutputStream(db_path); while (entry != null) < byte[] buffer = new byte[8192];// int count; while ((count = sevenZFile.read(buffer, 0, buffer.length)) >-1) < os.write(buffer, 0, count); >entry = sevenZFile.getNextEntry(); > sevenZFile.close(); os.close(); currentDateandTime = sdf.format(new Date()); log = mTVLog.getText().toString() + "\nEnd unzip 7zip" + currentDateandTime; mTVLog.setText(log); > 
 public static File getAssetFile(Context context, String asset_name, String name) throws IOException < File cacheFile = new File(context.getCacheDir(), name); try < InputStream inputStream = context.getAssets().open(asset_name); try < FileOutputStream outputStream = new FileOutputStream(cacheFile); try < byte[] buf = new byte[1024]; int len; while ((len = inputStream.read(buf)) >0) < outputStream.write(buf, 0, len); >> finally < outputStream.close(); >> finally < inputStream.close(); >> catch (IOException e) < throw new IOException("Could not open file" + asset_name, e); >return cacheFile; > 

Сначала мы копируем файл архива из asserts , а потом разархивируем при помощи SevenZFile . Он находится в пакете org.apache.commons.compress.archivers.sevenz; и поэтому перед его использованием нужно прописать в build.gradle зависимость: compile ‘org.apache.commons:commons-compress:1.8’.
Android Stuodio сама скачает библиотеки, а если они устарели, то подскажет о наличии обновления.

Вот экран работающего приложения:

Размер отладочной версии приложения получился 6,8 МБ.
А вот его размер в устройстве после распаковки:

Внимание вопрос кто в черном ящике что в кеше?

В заключении хочу сказать, что распаковка архивов занимает продолжительное время и поэтому нельзя его делать в основном (UI) потоке. Это приведет к подвисанию интерфейса. Во избежание этого можно задействовать AsyncTask , а лучше фоновый сервис т.к. пользователь может не дождаться распаковки и выйти, а вы получите ошибку (правда если не поставите костылей в методе onPostExecute).

Буду рад конструктивной критике в комментариях.

Источник

Оцените статью