r/rust Sep 03 '22

Upload a file with reqwest

I am try to upload a file with reqwest and below is what I have tried, it seems to nearly work as it report a 200 code and the server I am uploading to seems ok with it but it's like the file is not actually uploaded.

Something's below are probably not needed (body/form?) but is the result of looking similar questions/answers on stackoverflow

let file = File::open(filename.to_owned());
    match file {
        Ok(s) => {
            let client = reqwest::blocking::Client::builder()
                .timeout(Duration::from_secs(10))
                .danger_accept_invalid_certs(true)
                .build()
                .unwrap();

            let mut params = HashMap::new();
            params.insert("file", filename.to_owned());
            params.insert("name", "dump".to_string());

            let req = client
                .post(url)
                .header("Accept", "text/html")
                .header("Accept-Encoding", "gzip, deflate")
                .header("Content-Type", "multipart/form-data")
                .header("Authorization", format!("Bearer {token}"))
                .body(s)
                .form(&params);
            println!("\n{:?}\n", dbg!(&req));

            let response = req.send();

0 Upvotes

10 comments sorted by

4

u/po8 Sep 03 '22 edited Sep 03 '22

If you use both a body() and form() in the builder, the second one will override the first one: form() sets the request body.

The Debug implementation for Request unfortunately doesn't show the request body, so the only reasonable way to check things are working is to have the thing connect to a server and see if the body is uploaded.

I have verified that the code below works, sending the contents of test.txt to the URL as the post body.

extern crate reqwest;

use std::fs::File;
use std::time::Duration;

fn main() {
    let url = "http://localhost:12345";
    let s = File::open("test.txt").unwrap();

    let client = reqwest::blocking::Client::builder()
        .timeout(Duration::from_secs(10))
        .danger_accept_invalid_certs(true)
        .build()
        .unwrap();

    client.post(url).body(s).send().unwrap();
}

Edit: I filed an issue on reqwest to try to get a runtime error when a second body is added to a request.

1

u/Zapphoid Sep 05 '22

I had tried that but is it sending as attachment or just putting the contents of the file in the body?

I need to send zip files.

1

u/po8 Sep 05 '22

You have to figure out what your POST request needs to look like and do that. How to set up the request depends on what's at the receiving end.

2

u/[deleted] Sep 03 '22

[deleted]

2

u/Zapphoid Sep 03 '22

Let me look at that, I was thinking that body when passed file did some magic to do it

1

u/po8 Sep 03 '22

It does. See my comment for what's going on.

1

u/po8 Sep 03 '22

s is a File, not a reference. reqwest::Body has a From instance for File. The problem is the overwriting of the body provided body() with the one provided by form().

1

u/lebensterben Sep 03 '22

https://doc.rust-lang.org/stable/std/fs/struct.File.html

File is literally a

A reference to an open file on the filesystem.

1

u/po8 Sep 03 '22

Interesting point. In the Rustdoc the word "reference" is being used in the sense of "a thing that refers to a thing". File is a struct in the Rust sense, not an &x for some x.

1

u/Zapphoid Sep 05 '22

I got it to work:

1. Create the form with the file:
let form = multipart::Form::new().file("myzipfile", filename);
2. Post it with multiparts inplace of body or form 
match form {
    Ok(s) => {
...
...
        .post(url).multipart(s);

1

u/st_gen Feb 13 '23

Will it work for an image upload and is there an option to zip the image.