1. Обзор
В этом кратком руководстве мы обсудим, как заархивировать файл в архив и как распаковать архив – все это с помощью основных библиотек, предоставляемых Java.
Эти основные библиотеки являются частью java.util.zip пакет – где мы можем найти все утилиты, связанные с архивированием и распаковкой.
2. Zip-файл
Давайте сначала рассмотрим простую операцию – сжатие одного файла.
Для нашего примера здесь мы заархивируем файл с именем 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 Несколько Файлов
Далее давайте посмотрим, как заархивировать несколько файлов в один 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. Zip-каталог
Теперь давайте обсудим, как заархивировать весь каталог. Мы будем каталог 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) { // ... } zis.closeEntry(); zis.close(); } }
Внутри цикла while , мы переберем каждый ZipEntry и сначала проверим, является ли это каталогом . Если это так, то мы создадим каталог с помощью метода mkdirs () ; в противном случае мы продолжим создание файла:
while (zipEntry != null) { File newFile = newFile(destDir, zipEntry); if (zipEntry.isDirectory()) { if (!newFile.isDirectory() && !newFile.mkdirs()) { throw new IOException("Failed to create directory " + newFile); } } else { // fix for Windows-created archives File parent = newFile.getParentFile(); if (!parent.isDirectory() && !parent.mkdirs()) { throw new IOException("Failed to create directory " + parent); } // write file content FileOutputStream fos = new FileOutputStream(newFile); int len; while ((len = zis.read(buffer)) > 0) { fos.write(buffer, 0, len); } fos.close(); } zipEntry = zis.getNextEntry(); }
Здесь следует отметить, что в ветке else мы также сначала проверяем, существует ли родительский каталог файла. Это необходимо для архивов, созданных в Windows, где корневые каталоги не имеют соответствующей записи в zip-файле.
Еще один ключевой момент можно увидеть в методе newFile() :
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; }
Этот метод защищает от записи файлов в файловую систему за пределами целевой папки. Эта уязвимость называется Zip Slip, и вы можете подробнее об этом прочитать здесь .
6. Заключение
В этом руководстве показано, как мы можем использовать библиотеки Java для операций архивирования и распаковки файлов.
Реализацию этих примеров можно найти на GitHub .