如何使用 reqwest 发布文件?

问题描述 投票:0回答:4

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()?;

reqwest v0.11的最新文档不再包含此示例,并且在调用

body()
时尝试构建它失败并出现以下错误:

the trait `From<std::fs::File>` is not implemented for `Body`

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

rust reqwest
4个回答
25
投票

您链接到的具体示例是在使用异步的

reqwest
crate 之前的。如果您想使用确切的示例,则需要使用
reqwest::Client
,而不是
reqwest::blocking::Client
。这还需要启用
blocking
功能。

要明确的是,您实际上仍然可以找到该示例,它只是位于

reqwest::blocking::RequestBuilder
body()
方法的文档中。

// 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(())
}

另请查看

reqwest
Form
RequestBuilder
multipart()
方法,因为例如有一个
file()
方法。


如果您确实想使用异步,那么您可以使用

FramedRead
crate 中的
tokio-util
。连同
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
}

6
投票

如果您想使用

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"] }

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)
}

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);
    }
}

0
投票

板条箱streamer可以为您做到这一点,并启用功能

hyper

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 将以 1 兆字节块的形式传输您的文件


0
投票

示例代码

另请查看 reqwest 的 Form 和 RequestBuilder 的 multipart() 方法,因为其中有一个 file() 方法。

来自已接受的答案。为我工作。

    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)
    }
© www.soinside.com 2019 - 2024. All rights reserved.