Skip to content
Select theme

How to read and write a tar.gz archive

Short answer: Two crates, layered: tar for the archive format, flate2 for the gzip around it. Wrap a GzEncoder around your file and hand it to tar::Builder to write; wrap a GzDecoder in tar::Archive to read. Nothing loads the whole archive into memory — entries stream through as you go.

Start from a fresh project — cargo new tar-demo — and declare both crates:

Cargo.toml
[dependencies]
flate2 = "1.1.10"
tar = "0.4.46"

This builds a demo directory with two files and packs it into demo.tar.gz. The paths are relative, so the archive lands in the directory you run cargo run from:

packed demo/ -> demo.tar.gz

The layering is the whole API. GzEncoder wraps the File, so the gzip stream goes straight to disk — no intermediate .tar is ever written. Builder writes tar entries into that encoder: append_dir_all("demo", "demo") walks the directory recursively, the first argument being the name entries get inside the archive, the second what to walk on disk. For a single file there is append_path; Compression::default() is gzip level 6.

The last line is not ceremony. into_inner() takes the encoder back out of the builder, and finish() writes the gzip trailer and returns the file. Dropping the encoder would close the stream too — but it ignores errors on the way out, so a full disk would look like success. finish() is the version that reports them.

demo/
demo/notes.txt
demo/todo.txt

Archive::new accepts any reader. Iterating entries() decompresses and parses one entry at a time, in archive order, so a 2 GB tar.gz costs no more memory than this one. entry.path() returns a path borrowed from the entry — a Cow<Path> — which is why display() is called in the same expression; keep the path past the next loop iteration and the borrow checker will point you at to_path_buf().

hello from the archive

An entry is itself a Read, so read_to_string streams just that one file out of the archive — no copy of the rest is touched. To write to disk instead, entry.unpack("out/notes.txt")? extracts a single entry, tar.unpack("out")? extracts everything, and for an archive from a source you don’t trust, entry.unpack_in("out") refuses paths that climb out of out with a leading ../.