Tutorials

Gzip file compression with Rust

CM-IV
#file-compression#rust#how-to

Let’s assume you want to quickly compress multiple documents or files in one place on Linux. To accomplish this, utilize the tar crate to generate a tarball archive on the local file system. Subsequently, employ the gzip backend for compression, achieving a well-balanced combination of speed and reduced storage space for the final output. This example employs the flate2 crate, specifically utilizing write::GzEncoder, to craft the gzip archive.

For obtaining a Vec<PathBuf>—a vector containing file paths for the files intended for compression—you can make use of the fltk crate alongside the dialog::NativeFileChooserType::BrowseMultiFile file picker. The fltk crate functions as a lightweight GUI library, which I personally prefer for my projects due to its simplicity and powerful capabilities. It proves to be both easy to setup and work with, offering a wide range of builtin components for use. In this process, I will also incorporate familiar crates I’ve previously utilized: camino and dirs for handling file system directories and transforming them into UTF-8 compatible PathBufs. Once the files are traversed and formed into a tarball, the resulting tarball data (a byte vector) is then employed alongside the aforementioned GzEncoder to generate a compressed gzip tarball.

The following code block shows the project implementation:

use std::{fs::File, io::Write};

use camino::Utf8PathBuf;
use flate2::{write::GzEncoder, Compression};
use fltk::{prelude::*, *};

fn main() {
    let app = app::App::default();
    let mut wind = window::Window::default().with_size(400, 300);

    let mut btn = button::Button::default()
        .with_size(80, 30)
        .with_label("Select file")
        .center_of_parent();

    wind.end();
    wind.show();

    btn.set_callback(|_| {
        let mut dialog = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseMultiFile);
        dialog.show();

        let mut ar = tar::Builder::new(Vec::with_capacity(dialog.filenames().len()));

        for path in dialog.filenames() {
            let utf8_pathbuf = Utf8PathBuf::from_path_buf(path).unwrap();

            let mut input_file = File::open(utf8_pathbuf.as_path()).unwrap();

            ar.append_file(utf8_pathbuf.file_name().unwrap(), &mut input_file).unwrap();
        }

        let tar_data = ar.get_ref();

        let dir = dirs::download_dir().expect("Couldn't get downloads dir!");
        let mut output_path = Utf8PathBuf::from_path_buf(dir).unwrap();
        output_path.push("comp.tar.gz");

        let output_file = File::create(&output_path).unwrap();
        let mut gz_encoder = GzEncoder::new(output_file, Compression::default());
        gz_encoder.write_all(&tar_data).unwrap();
        gz_encoder.finish().unwrap();

        dialog::message_default("Files compressed!");
    });

    app.run().unwrap();
}

There is only one single button shown when the program is running that prompts the user to select a file (or multiple files) to compress via the set callback function within the code. This is where the meat of the application resides, as the application comes to life once that button is pressed.

← Back to Blog