Skip to main content

Download Binary File/Image (Rust)

use error_chain::error_chain;
use std::io::copy;
use std::fs::File;
use tempfile::Builder;
use std::io::Cursor;

error_chain! {
foreign_links {
Io(std::io::Error);
HttpRequest(reqwest::Error);
}
}

#[tokio::main]
async fn main() -> Result<()> {
let tmp_dir = Builder::new().prefix("example").tempdir()?;
let target = "https://www.rust-lang.org/logos/rust-logo-512x512.png";
let response = reqwest::get(target).await?;

let mut dest = {
let fname = response
.url()
.path_segments()
.and_then(|segments| segments.last())
.and_then(|name| if name.is_empty() { None } else { Some(name) })
.unwrap_or("tmp.bin");

println!("file to download: '{}'", fname);
// let fname = tmp_dir.path().join(fname); ! this line is commented out as temp files are removed on exit.
// This will download the file in cwd, so we can verify the content.
println!("will be located under: '{:?}'", fname);
File::create(fname)?
};
// let content = response.text().await?;
// copy(&mut content.as_bytes(), &mut dest)?;

let mut content = Cursor::new(response.bytes().await?);
copy(&mut content, &mut dest)?;
Ok(())
}

The Official Cook Book Sample Code is wrong. Doesn't work. Instead of using .text(), use .bytes() and wrapped with Cursor.

Cursor is the main point. An experienced developer (but beginner in Rust) knows that converting binary file content to text then bytes is dangerous, due to encoding issue; but wouldn't know about Cursor.

Error Explained

std::io::Read requires Read, which Bytes doesn't have. So Cursor is needed.

Weird design for developer from another language.

the trait bound `bytes::bytes::Bytes: std::io::Read` is not satisfied
the trait `std::io::Read` is not implemented for `bytes::bytes::Bytes
main.rs(37, 5): required by a bound introduced by this call
copy.rs(55, 8): required by a bound in `std::io::copy`
No quick fixes available

Reference