如何使用 reqwest 发布文件?

Eri*_*ric 2 rust reqwest

reqwest v0.9.18的文档显示了以下发布文件的示例:

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()?;
Run Code Online (Sandbox Code Playgroud)

reqwest v0.11的最新文档不再包含此示例,并且在调用时尝试构建它失败并出现以下错误body()

the trait `From<std::fs::File>` is not implemented for `Body`
Run Code Online (Sandbox Code Playgroud)

发送文件的更新方法是什么?

Val*_*tin 5

您链接到的特定示例是在使用 async的reqwest板条箱之前。如果你想使用那个确切的例子,那么reqwest::Client你需要使用reqwest::blocking::Client. 这也需要启用该blocking功能。

需要明确的是,实际上你可以仍然发现,例如,它只是坐落在文档的reqwest::blocking::RequestBuilderbody()方法来代替。

// 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(())
}
Run Code Online (Sandbox Code Playgroud)

还要检查reqwest'sFormRequestBuilder'smultipart()方法,因为例如有一个file()方法。


如果您确实想使用异步,那么您可以使用FramedReadfrom tokio-utilcrate。随着TryStreamExt特性,来自futures板条箱

只要确保启用stream的功能reqwest,以及codec为特征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
}
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您想使用multipart/form-data并且已经在使用 Tokio,那么这种方法可以帮助您。

1. 设置依赖项

# 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"] }
Run Code Online (Sandbox Code Playgroud)

2.使用上传文件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)
}
Run Code Online (Sandbox Code Playgroud)

3. 单元测试

#[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);
    }
}
Run Code Online (Sandbox Code Playgroud)