Перейти к основному содержимому

Сжатие и распаковка в Java

· 4 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Обзор

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

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

2. Заархивируйте файл

Во-первых, давайте рассмотрим простую операцию, сжатие одного файла.

В нашем примере мы заархивируем файл с именем test1.txt в архив с именем сжатым.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 {
List<String> srcFiles = 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. Разархивируйте архив

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

В этом примере мы разархивируем сжатый.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 .