Работа с ZIP/архивами в Kotlin
В современной разработке на Kotlin (как для JVM, так и для Android) часто возникает необходимость упаковывать файлы в ZIP-архивы или извлекать данные из них. Стандартная библиотека Java (java.util.zip) предоставляет все необходимые классы, которые прекрасно работают и в Kotlin. В этой статье мы подробно разберём три основные операции: создание архива, чтение содержимого и распаковку файлов.
1. Создание ZIP-архива (упаковка файлов)
Для создания архива используется ZipOutputStream. Каждый файл добавляется как отдельная запись (ZipEntry). Рассмотрим пример, который упаковывает несколько файлов в один архив.
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
fun createZip(
filesToZip: List<File>,
outputZipFile: File
) {
ZipOutputStream(FileOutputStream(outputZipFile)).use { zipOut ->
for (file in filesToZip) {
// Создаём запись с именем файла
val entry = ZipEntry(file.name)
zipOut.putNextEntry(entry)
// Читаем исходный файл и пишем в архив
FileInputStream(file).use { fis ->
fis.copyTo(zipOut)
}
zipOut.closeEntry()
}
}
}
fun main() {
val files = listOf(
File("document.txt"),
File("image.png"),
File("data.json")
)
val archive = File("my_archive.zip")
createZip(files, archive)
println("Архив создан: ${archive.absolutePath}")
}
Обратите внимание на использование .use { ... } — это безопасное закрытие ресурсов, эквивалентное try-with-resources в Java. Если нужно упаковать файлы из подкаталогов, придётся рекурсивно обходить директории и сохранять относительные пути в имени ZipEntry.
2. Чтение содержимого архива (просмотр списка файлов)
Иногда требуется только посмотреть, какие файлы находятся внутри архива, без их извлечения. Для этого используется ZipFile или ZipInputStream. Первый вариант удобнее, если архив уже существует и не слишком велик.
import java.io.File
import java.util.zip.ZipFile
fun listZipContent(zipFile: File) {
ZipFile(zipFile).use { zip ->
val entries = zip.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
println("${entry.name} (${entry.size} байт)")
}
}
}
fun main() {
val archive = File("my_archive.zip")
if (archive.exists()) {
listZipContent(archive)
} else {
println("Файл не найден")
}
}
Метод entries() возвращает перечисление всех записей. Для каждой записи доступны имя, размер, дата модификации и флаги (например, является ли запись директорией).
3. Распаковка ZIP-архива (извлечение файлов)
Для извлечения файлов удобно использовать ZipInputStream. Он читает архив последовательно, запись за записью. Ниже приведён пример полной распаковки в указанную папку.
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.ZipInputStream
fun extractZip(
zipFile: File,
outputDir: File
) {
if (!outputDir.exists()) {
outputDir.mkdirs()
}
ZipInputStream(FileInputStream(zipFile)).use { zis ->
var entry = zis.nextEntry
while (entry != null) {
val targetFile = File(outputDir, entry.name)
// Создаём родительские директории, если нужно
targetFile.parentFile?.mkdirs()
// Если запись — директория, просто создаём её
if (entry.isDirectory) {
targetFile.mkdirs()
} else {
// Извлекаем файл
FileOutputStream(targetFile).use { fos ->
zis.copyTo(fos)
}
}
zis.closeEntry()
entry = zis.nextEntry
}
}
}
fun main() {
val archive = File("my_archive.zip")
val outputFolder = File("extracted_files")
extractZip(archive, outputFolder)
println("Распаковка завершена в: ${outputFolder.absolutePath}")
}
Важно: при распаковке всегда проверяйте entry.isDirectory, чтобы случайно не создать файл с именем папки. Также стоит учитывать потенциальную уязвимость «Zip Slip» — злоумышленник может указать в имени записи путь вида ../../etc/passwd. Для защиты нужно проверять, что целевой файл находится внутри outputDir.
4. Дополнительные возможности
- Добавление файлов в существующий архив — напрямую это невозможно, так как ZIP — последовательный формат. Приходится перес
Также в библиотеке
Авторизация и аутентификация в Go (Golang): JWT, OAuth2 и сессии
Реактивное программирование в Swift: Combine и RxSwift для iOS/macOS
Криптография в Java: шифрование и хеширование данных