rgreinho / trauma

Tokio Rust Asynchronous Universal download MAnager
MIT License
126 stars 10 forks source link

Saving a file by the name sent by the server #68

Open p32929 opened 1 year ago

p32929 commented 1 year ago

Hi, first of all, thanks a lot for creating this package/crate ( I'm kinda new to rust, so ). I was just wondering if there's any way to download a file by the name sent by the server. for instance:

if we try to download file from this link: https://github.com/seanmonstar/reqwest/archive/refs/tags/v0.11.9.zip it can get the file name & the extension from the URL.

But if we try to download from this link: https://code.visualstudio.com/sha/download?build=stable&os=win32-x64-user Even though the server sends the file name as VSCodeUserSetup-x64-1.80.1_2.exe but it gets saved as download without any extension, like this: Screenshot (3)

Screenshot (4)

here's a screenshot from IDM as it can grab the file name sent by the server: Screenshot (2)

is there any way I can save the file as the name sent by the server? if not, I'd love to implement the feature and make a pull request. Thanks

p32929 commented 1 year ago

sorry, mistakenly labeled as bug but I guess, it should've been in the feedback/question

rawhuul commented 1 year ago

Hey, I also witnessed same thing while using trauma in my app, is there any fix or patch? However, it shouldn't be the intended behavior of any download manager.

rgreinho commented 1 year ago

Hi @p32929!

Thanks for the report. Indeed this behavior is not implemented in trauma, but I agree it could be a good addition. So if you want to tackle it, please go ahead.

I am not entirely sure how you would achieve this. My first guess would be to follow the redirect. But there may be other headers that may help.

p32929 commented 1 year ago

So, this piece of code actually helped me:

use reqwest::header::CONTENT_DISPOSITION;
use trauma::Error;

#[tokio::main]
async fn main() -> Result<(), Error> {
    show_file_name("https://code.visualstudio.com/sha/download?build=stable&os=win32-x64-user")
        .await
        .unwrap();
    show_file_name("https://github.com/seanmonstar/reqwest/archive/refs/tags/v0.11.9.zip")
        .await
        .unwrap();
    Ok(())
}

fn extract_file_name_from_header(header_value: &str) -> Option<String> {
    // Check if the header contains "filename="
    if let Some(index) = header_value.find("filename=") {
        // Extract the value after "filename="
        let file_name = &header_value[index + 9..];
        // Remove surrounding quotes if present
        let trimmed_file_name = file_name.trim_matches('"');
        // Return the extracted file name
        return Some(trimmed_file_name.to_string());
    }
    None
}

async fn show_file_name(url_string: &str) -> Result<(), Error> {
    // Make an HTTP GET request
    let response = reqwest::get(url_string).await?;

    // Check if the response contains the file name in the Content-Disposition header
    if let Some(content_disposition) = response.headers().get(CONTENT_DISPOSITION) {
        if let Some(file_name) =
            extract_file_name_from_header(content_disposition.to_str().unwrap_or(""))
        {
            println!("File Name: {}", file_name);
        }
    }
    Ok(())
}

& here's the cargo.toml file:

[package]
name = "abcde"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
trauma = "2"
tokio = "1.29.1"
reqwest = "0.11.18"

& here's a screenshot of the file names printed: Screenshot (7)

Pardon the ugly codes, as I'm very new to Rust

Vzz1c commented 10 months ago

The filename is usually provided via the response header field Content-Disposition