Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Download large files in rust with progress bar using reqwest, future_util and indicatif
// you need this in your cargo.toml
// reqwest = { version = "0.11.3", features = ["stream"] }
// futures-util = "0.3.14"
// indicatif = "0.15.0"
use std::cmp::min;
use std::fs::File;
use std::io::Write;
use reqwest::Client;
use indicatif::{ProgressBar, ProgressStyle};
use futures_util::StreamExt;
pub async fn download_file(client: &Client, url: &str, path: &str) -> Result<(), String> {
// Reqwest setup
let res = client
.get(url)
.send()
.await
.or(Err(format!("Failed to GET from '{}'", &url)))?;
let total_size = res
.content_length()
.ok_or(format!("Failed to get content length from '{}'", &url))?;
// Indicatif setup
let pb = ProgressBar::new(total_size);
pb.set_style(ProgressStyle::default_bar()
.template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")
.progress_chars("#>-"));
pb.set_message(&format!("Downloading {}", url));
// download chunks
let mut file = File::create(path).or(Err(format!("Failed to create file '{}'", path)))?;
let mut downloaded: u64 = 0;
let mut stream = res.bytes_stream();
while let Some(item) = stream.next().await {
let chunk = item.or(Err(format!("Error while downloading file")))?;
file.write_all(&chunk)
.or(Err(format!("Error while writing to file")))?;
let new = min(downloaded + (chunk.len() as u64), total_size);
downloaded = new;
pb.set_position(new);
}
pb.finish_with_message(&format!("Downloaded {} to {}", url, path));
return Ok(());
}
@mihaigalos
Copy link

mihaigalos commented Dec 8, 2021

@ozkanpakdil : have a look at the implementation of aim for how upload with a progress bar is handled.
I'm adding more features in the coming weeks/months.

@firasuke
Copy link

firasuke commented Jan 2, 2022

@giuliano-oliveira @mihaigalos can this be used to download multiple files concurrently?

@orhun
Copy link

orhun commented Feb 4, 2022

You might want to consider using write_all instead of write: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount

@lennartkloock
Copy link

lennartkloock commented Feb 10, 2022

Yes, I agree. It is important to use write_all instead of write, otherwise it won't work

@giuliano-oliveira
Copy link
Author

giuliano-oliveira commented Feb 10, 2022

Makes perfect sense, thanks @orhun @lennartkloock

@Tails
Copy link

Tails commented Jun 23, 2022

Does .get().send().await not immediately retrieve the whole response body to memory?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment