3

I am following the code mentioned in Rust Cookbook at https://rust-lang-nursery.github.io/rust-cookbook/web/clients/download.html to download a file in async way by HTTP GET request.

My code is as follows:

#[tokio::main]
async fn main() -> Result<()> {

    let object_path = "logos/rust-logo-512x512.png";
    let target = format!("https://www.rust-lang.org/{}", object_path);  
    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 object_prefix = &object_path[..object_path.rfind('/').unwrap()];
        let object_name = &object_path[object_path.rfind('/').unwrap()+1..];
        let output_dir = format!("{}/{}", env::current_dir().unwrap().to_str().unwrap().to_string(), object_prefix);
        fs::create_dir_all(output_dir.clone())?;

        println!("will be located under: '{}'", output_dir.clone());
                
        let output_fname = format!("{}/{}", output_dir, object_name);
        println!("Creating the file {}", output_fname);
        
        File::create(output_fname)?
        
    };
    let content =  response.text().await?;
    copy(&mut content.as_bytes(), &mut dest)?;
    Ok(())
}

It creates the directories & downloads the file. However, when I open the file, it displays corrupt file error I have tried to use some other URL also, but the corrupt file issue is still there

Am I missing something in the code?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 6
    You're downloading the response as text, so naturally it won't work because PNG files are binary. – hoz Sep 14 '20 at 07:34
  • 1
    use [`bytes`](https://docs.rs/reqwest/0.10.8/reqwest/struct.Response.html#method.bytes) instead of `text` – Netwave Sep 14 '20 at 07:46
  • I tried ```let content = response.bytes().await?; copy(&mut content, &mut dest)?;``` but it gives error ``` copy(&mut content, &mut dest)?; the trait std::io::Read is not implemented for bytes::bytes::Bytes ``` –  Sep 14 '20 at 07:55
  • @test, use [`io::Cursor`](https://doc.rust-lang.org/std/io/struct.Cursor.html) – Netwave Sep 14 '20 at 08:10

2 Answers2

5

Just using bytes and Cursor work too, and it is simpler:

let mut content =  Cursor::new(response.bytes().await?);
copy(&mut content, &mut dest)?;
Netwave
  • 40,134
  • 6
  • 50
  • 93
2

Replacing

let content =  response.text().await?;
copy(&mut content.as_bytes(), &mut dest)?;

by

let content =  response.bytes().await?;
    
let mut pos = 0;
while pos < content.len() {
    let bytes_written = dest.write(&content[pos..])?;
    pos += bytes_written;
}

worked! :)

Please reply if this code is inefficient Thanks to all for help.

  • 2
    Simpler to use [`write_all`](https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all): `dest.write_all (&content)?;` instead of doing the loop by hand. – Jmb Sep 14 '20 at 10:18