Работа с ZIP-архивами в Rust с помощью библиотеки zip
При разработке на Rust часто возникает необходимость упаковывать файлы в ZIP-архивы или извлекать данные из них. Это может понадобиться для распространения программного обеспечения, создания резервных копий, обработки загруженных пользователем архивов или сборки пакетов. Стандартная библиотека Rust не включает встроенных средств для работы с ZIP, однако экосистема предлагает мощную и удобную библиотеку zip.
В этой статье мы подробно разберём, как использовать библиотеку zip для создания, чтения и извлечения ZIP-архивов. Вы узнаете, как установить библиотеку, работать с файлами и директориями, обрабатывать ошибки и управлять сжатием.
Установка библиотеки zip
Для начала добавьте зависимость в файл Cargo.toml вашего проекта:
[dependencies]
zip = "2.2"
Библиотека активно поддерживается и использует современные возможности Rust. После добавления зависимости вы можете импортировать её в код:
use zip::ZipWriter;
use zip::read::ZipArchive;
use std::io::{Read, Write, BufWriter, BufReader};
use std::fs::{self, File};
Создание ZIP-архива
Создание архива состоит из нескольких шагов: открытие файла для записи, инициализация ZipWriter, добавление файлов и завершение записи. Рассмотрим пример, который упаковывает один текстовый файл:
use std::fs::File;
use zip::ZipWriter;
use zip::write::FileOptions;
use std::io::Write;
fn create_archive() -> zip::result::ZipResult<()> {
let file = File::create("example.zip")?;
let mut zip = ZipWriter::new(file);
let options = FileOptions::default()
.compression_method(zip::CompressionMethod::Deflated)
.unix_permissions(0o755);
zip.start_file("hello.txt", options)?;
zip.write_all(b"Hello, Rust ZIP!")?;
zip.finish()?;
Ok(())
}
Здесь мы:
- Создаём файл
example.zipв текущей директории. - Инициализируем
ZipWriter. - Настраиваем параметры файла: метод сжатия
Deflated(стандартное сжатие) и права доступа. - Добавляем файл
hello.txtи записываем в него содержимое. - Завершаем запись вызовом
finish().
Для добавления нескольких файлов или целой директории используйте цикл по файлам в папке:
fn add_directory(zip: &mut ZipWriter<File>, dir_path: &str) -> zip::result::ZipResult<()> {
for entry in walkdir::WalkDir::new(dir_path) {
let entry = entry?;
let path = entry.path();
if path.is_file() {
let name = path.strip_prefix(dir_path).unwrap().to_str().unwrap();
zip.start_file(name, FileOptions::default())?;
let mut f = File::open(path)?;
std::io::copy(&mut f, zip)?;
}
}
Ok(())
}
Обратите внимание: для рекурсивного обхода директорий удобно использовать крейт walkdir, но его нужно добавить отдельно.
Чтение и извлечение ZIP-архива
Для чтения архива используется ZipArchive. Ниже приведён пример, который выводит список файлов в архиве и извлекает их в указанную папку:
use zip::ZipArchive;
use std::io::Read;
fn extract_archive(archive_path: &str, output_dir: &str) -> zip::result::ZipResult<()> {
let file = File::open(archive_path)?;
let mut archive = ZipArchive::new(file)?;
for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
let entry_name = entry.name().to_string();
println!("Извлекается: {}", entry_name);
// Создаём полный путь к файлу
let output_path = format!("{}/{}", output_dir, entry_name);
if entry_name.ends_with('/') {
fs::create_dir_all(&output_path)?;
} else {
if let Some(parent) = std::path::Path::new(&output_path).parent() {
fs::create_dir_all(parent)?;
}
let mut outfile = File::create(&output_path)?;
std::io::copy(&mut entry, &mut outfile)?;
}
}
Ok(())
}
Ключевые моменты:
archive.len()возвращает количество записей (файлов и директорий) в архиве.archive.by_index(i)даёт доступ к каждой запи