2 releases
new 0.1.1 | Mar 21, 2025 |
---|---|
0.1.0 | Mar 19, 2025 |
#155 in HTTP client
46 downloads per month
39KB
587 lines
http-client-multipart
This crate provides multipart request support for the http-client
crate, enabling you to easily create and send multipart HTTP requests with file uploads and form data. It is designed to be client-agnostic, working seamlessly with any HttpClient
implementation.
Features
- Client Agnostic: Works with any HTTP client that implements the
http-client
'sHttpClient
trait. - File Uploads: Easily add files to your multipart requests.
- Form Fields: Include standard form fields in your requests.
- Correct Headers: Automatically sets the
Content-Type
header with the correct boundary. - Easy to Use: A simple and ergonomic API.
Installation
Add http-client-multipart
to your Cargo.toml
:
[dependencies]
http-client-multipart = "0.1.0" # Replace with the latest version
http-client = "6.5.3" # Ensure you have the base http-client crate
async-std = "1.6.0" # Or tokio, or any async runtime you prefer
Note: You also need to include a concrete implementation of the http-client
's HttpClient
trait (if you haven't already). Some popular choices:
http-client
withh1_client
(default, usesasync-h1
):http-client = { version = "6.5.3", features = ["h1_client", "native-tls"] }
http-client
withisahc
(curl_client
feature):http-client = { version = "6.5.3", features = ["curl_client"] }
http-client
withhyper
(hyper_client
feature):http-client = { version = "6.5.3", features = ["hyper_client"] }
Usage
Here's a detailed guide on how to use the http-client-multipart
crate:
1. Import the necessary crates and modules:
use http_client::{HttpClient, Request};
use http_types::{Method, Url};
use http_client_multipart::Multipart; // Import the Multipart struct
use http_client_multipart::RequestMultipartExt; // Import the extension trait (Optional)
use async_std::task; // Or tokio, or any async runtime you prefer
2. Create a Multipart
instance:
let mut multipart = Multipart::new();
This creates a new multipart form with a randomly generated boundary. The boundary is used to separate each part of the multipart data.
3. Add fields to the Multipart
form:
Adding Text Fields:
Use the add_text
method to add standard form fields:
multipart.add_text("name", "John Doe");
multipart.add_text("age", "30");
Adding File Fields (from Path - Simplest Approach):
Use the add_file
(async version) method to add files from their paths on the filesystem:
multipart.add_file("avatar", "path/to/your/image.jpg").await?; // requires an async context (.await)
This automatically infers the filename and content type based on the file's path. It reads the file asynchronously, making it suitable for async contexts. The add_file
function needs to be awaited as it is an async function.
Adding File Fields (from Readers):
If you already have file data in memory or want more control over the filename and content type, you can add file fields using add_async_read
or add_sync_read
.
-
add_async_read
(Asynchronous Reader - recommended for async contexts):use async_std::fs::File as AsyncFile; use async_std::io::BufReader; let file = AsyncFile::open("path/to/your/file.txt").await?; let buf_reader = BufReader::new(file); // Wrap the async file with a buffered reader multipart.add_async_read("document", "file.txt", "text/plain", buf_reader).await?;
This method takes an asynchronous reader (
impl AsyncRead + Unpin + Send + 'static
) as input, along with the desired filename and content type. The data is read asynchronously into the body. Theadd_async_read
function needs to be awaited as it is an async function. -
add_sync_read
(Synchronous Reader - use with caution in async contexts):use std::fs::File; let file = File::open("path/to/your/file.txt")?; multipart.add_sync_read("config", "config.txt", "text/plain", file)?;
This method takes a synchronous reader (
impl Read + Seek + Send + 'static
) as input, along with the desired filename and content type. Use this in synchronous contexts, or if you are very careful about thread blocking in async. -
add_file_from_sync
(Convenience forFile
objects - synchronous):use std::fs::File; let file = File::open("path/to/your/file.txt")?; multipart.add_file_from_sync("archive", "data.zip", "application/zip", file)?;
This is a shorthand for creating a
File
object directly and adding it.
4. Create an http-client
Request
:
let url = "https://httpbin.org/post".parse::<Url>()?; // Replace with your API endpoint
let mut req = Request::new(Method::Post, url);
5. Set the Multipart
data as the request body:
There are two ways to do this:
Method 1: Using set_request(req: &mut Request)
(Mutates Existing Request - Preferred):
This is the recommended approach because it encapsulates all the logic within the Multipart
struct:
multipart.set_request(&mut req)?;
This method will:
- Convert the
Multipart
form into aBody
. - Set the
Content-Type
header of the request tomultipart/form-data
with the correct boundary. - Set the body of the request to the converted body.
Method 2: Using the RequestMultipartExt
trait (Extension Method):
This approach adds an extension method to the Request
object:
use http_client_multipart::RequestMultipartExt;
req.set_multipart_body(multipart)?;
Both achieve the same outcome, but set_request
offers better encapsulation.
6. Create an HttpClient
and send the request:
use http_client::h1::H1Client as Client; // Example: Using h1_client
let client = Client::new();
let mut response = client.send(req).await?;
Remember to choose a concrete HttpClient
implementation based on your needs (e.g., H1Client
, IsahcClient
, HyperClient
).
7. Handle the response:
let body = response.body_string().await?;
println!("{}", body);
Complete Example (using async-std and h1_client):
use http_client::{HttpClient, Request};
use http_types::{Method, Url, Result};
use http_client_multipart::Multipart;
use http_client_multipart::RequestMultipartExt;
use async_std::task;
use http_client::h1::H1Client as Client;
async fn send_multipart_request() -> Result<()> {
// 1. Create a Multipart instance
let mut multipart = Multipart::new();
// 2. Add fields to the Multipart form
multipart.add_text("name", "John Doe");
multipart.add_text("age", "30");
// Add a file (assuming you have a file named "image.jpg" in the same directory)
multipart.add_file("avatar", "Cargo.toml").await?;
// 3. Create an http-client Request
let url = "https://httpbin.org/post".parse::<Url>()?; // Replace with your API endpoint
let mut req = Request::new(Method::Post, url);
// 4. Set the Multipart data as the request body using set_request()
multipart.set_request(&mut req)?;
// 5. Create an HttpClient and send the request
let client = Client::new(); // Or any other HttpClient implementation
let mut response = client.send(req).await?;
// 6. Handle the response
let body = response.body_string().await?;
println!("{}", body);
Ok(())
}
fn main() -> Result<()> {
task::block_on(async {
send_multipart_request().await
})
}
Notes
- Error Handling: The examples above use
?
for error propagation. In a real application, handle errors gracefully. - File Paths: Ensure that the file paths you provide to
add_file
are correct and accessible. - Content Types: While automatic content type detection is provided, you might need to specify the content type explicitly for certain file types using
add_async_read
oradd_sync_read
if the automatic detection is inaccurate. - Performance: For very large files, consider streaming the file data instead of reading it all into memory at once, using
add_async_read
.
License
This crate is licensed under the MIT License.
Dependencies
~8MB
~131K SLoC