El crate zip escribe y lee archivos comprimidos sin depender de nada del sistema. Lo que conviene entender es que un ZIP no guarda carpetas de verdad: la jerarquía está en el nombre de cada entrada.
También te puede interesar
La dependencia
cargo add zip
[dependencies] zip = "8.6.0"
Crear el ZIP y volver a leerlo
use std::fs::File;
use std::io::{Read, Write};
use zip::write::SimpleFileOptions;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut zip = zip::ZipWriter::new(File::create("copia.zip")?);
let opciones = SimpleFileOptions::default();
for (nombre, contenido) in [("notas.txt", "primera nota"), ("datos/lista.txt", "a,b,c")] {
zip.start_file(nombre, opciones)?;
zip.write_all(contenido.as_bytes())?;
}
zip.finish()?;
println!("copia.zip creado");
let mut archivo = zip::ZipArchive::new(File::open("copia.zip")?)?;
println!("contiene {} entradas:", archivo.len());
for i in 0..archivo.len() {
let mut e = archivo.by_index(i)?;
let mut texto = String::new();
e.read_to_string(&mut texto)?;
println!(" {} ({} bytes): {}", e.name(), e.size(), texto);
}
Ok(())
}
salidacopia.zip creado contiene 2 entradas: notas.txt (12 bytes): primera nota datos/lista.txt (5 bytes): a,b,c
Fíjate en la segunda entrada: datos/lista.txt. No hubo que crear ninguna carpeta datos, basta con poner la barra en el nombre. Así funciona el formato, y por eso al extraer hay que crear las carpetas intermedias antes de escribir cada archivo.
finish() es obligatorio: escribe el índice central del ZIP, que va al final del archivo. Sin esa llamada el archivo queda incompleto y ningún descompresor lo abre.
../../etc/algo y, si escribes donde diga el nombre sin comprobarlo, un ZIP malicioso te deja archivos fuera de la carpeta de destino. Es un problema clásico, conocido como Zip Slip. El crate ofrece enclosed_name(), que devuelve None cuando el nombre se sale de la carpeta; conviene usar ese y no name() al extraer a disco.Opciones y variantes
| Qué quieres | Cómo |
|---|---|
| Añadir un archivo desde disco | start_file y copiar con io::copy |
| Una carpeta vacía dentro del ZIP | add_directory("nombre/", opciones) |
| Guardar sin comprimir | .compression_method(CompressionMethod::Stored) |
| Nombre seguro al extraer | e.enclosed_name() |
| Extraer todo de golpe | archivo.extract(carpeta) |
| Trabajar en memoria | Cursor<Vec<u8>> en vez de File |
El código de esta página se compiló y ejecutó con rustc 1.96.1 y zip 8.6.0 antes de publicarla; la salida está copiada de la ejecución real. Documentación oficial: zip en docs.rs.