Reputation: 11662
The documentation for reqwest v0.9.18 shows the following example of posting a file:
let file = fs::File::open("from_a_file.txt")?;
let client = reqwest::Client::new();
let res = client.post("http://httpbin.org/post")
.body(file)
.send()?;
The latest documentation for reqwest v0.11 no longer includes this example, and trying to build it fails with the following error when calling body()
:
the trait `From<std::fs::File>` is not implemented for `Body`
What is the updated method for sending a file?
Upvotes: 17
Views: 14894
Reputation: 1
An example code for
Also check out reqwest's Form and RequestBuilder's multipart() method, as there for instance is a file() method.
from the accepted answer. Worked for me.
use anyhow::Result;
use reqwest::{multipart, StatusCode};
#[tokio::test]
async fn upload_file()-> Result<()> {
let form = multipart::Form::new().file("file", "./Cargo.toml").await?;
let client = reqwest::Client::new();
let ret = client
.post("http://localhost:6688/api/files")
.multipart(form)
.send()
.await?;
assert_eq!(ret.status(), StatusCode::OK);
let chat_files = ret.json().await?;
Ok(chat_files)
}
Upvotes: -1
Reputation: 332
If you want to use multipart/form-data
and you are using Tokio
already, this approach could help you.
# Cargo.toml
[dependencies]
tokio = { version = "1.19", features = ["macros", "rt-multi-thread"] }
reqwest = { version = "0.11.11", features = ["stream","multipart","json"] }
tokio-util = { version = "0.7.3", features = ["codec"] }
multipart/form-data
use reqwest::{multipart, Body, Client};
use tokio::fs::File;
use tokio_util::codec::{BytesCodec, FramedRead};
async fn reqwest_multipart_form(url: &str) -> anyhow::Result<String> {
let client = Client::new();
let file = File::open(".gitignore").await?;
// read file body stream
let stream = FramedRead::new(file, BytesCodec::new());
let file_body = Body::wrap_stream(stream);
//make form part of file
let some_file = multipart::Part::stream(file_body)
.file_name("gitignore.txt")
.mime_str("text/plain")?;
//create the multipart form
let form = multipart::Form::new()
.text("username", "seanmonstar")
.text("password", "secret")
.part("file", some_file);
//send request
let response = client.post(url).multipart(form).send().await?;
let result = response.text().await?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_post_form_file() {
let url = "http://httpbin.org/post?a=1&b=true";
let get_json = reqwest_multipart_form(url).await.unwrap();
println!("users: {:#?}", get_json);
}
}
Upvotes: 7
Reputation: 561
the crate streamer can do that for you with feature hyper
enabled:
use hyper::{Body, Request}:
let file = File::open("from_a_file.txt").unwrap();
let mut streaming = Streamer::new(file)
// optional, set the field name
// streaming.meta.set_name("txt");
// optional, set the file name
streaming.meta.set_filename("from_a_file.txt");
// length sent as a chunk, the default is 64kB if not set
streaming.meta.set_buf_len(1024 * 1024);
let body: Body = streaming.streaming();
// build a request
let request: Request<Body> = Request::post("<uri-here>").body(body).expect("failed to build a request");
streamer will stream your file in 1 Mega-bytes chunks
Upvotes: 0
Reputation: 26197
The specific example you're linking to, was prior to the reqwest
crate using async. If you want to use that exact example, then instead of reqwest::Client
, you need to use reqwest::blocking::Client
. This also requires enabling the blocking
feature.
To be clear, you can actually still find that example, it's just located in the docs for reqwest::blocking::RequestBuilder
's body()
method instead.
// reqwest = { version = "0.11", features = ["blocking"] }
use reqwest::blocking::Client;
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("from_a_file.txt")?;
let client = Client::new();
let res = client.post("http://httpbin.org/post")
.body(file)
.send()?;
Ok(())
}
Also check out reqwest
's Form
and RequestBuilder
's multipart()
method, as there for instance is a file()
method.
If you do want to use async, then you can use FramedRead
from the tokio-util
crate. Along with the TryStreamExt
trait, from the futures
crate.
Just make sure to enable the stream
feature for reqwest
, and the codec
feature for tokio-util
.
// futures = "0.3"
use futures::stream::TryStreamExt;
// reqwest = { version = "0.11", features = ["stream"] }
use reqwest::{Body, Client};
// tokio = { version = "1.0", features = ["full"] }
use tokio::fs::File;
// tokio-util = { version = "0.6", features = ["codec"] }
use tokio_util::codec::{BytesCodec, FramedRead};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("from_a_file.txt").await?;
let client = reqwest::Client::new();
let res = client
.post("http://httpbin.org/post")
.body(file_to_body(file))
.send()
.await?;
Ok(())
}
fn file_to_body(file: File) -> Body {
let stream = FramedRead::new(file, BytesCodec::new());
let body = Body::wrap_stream(stream);
body
}
Upvotes: 25