Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multipart/Form-Data

aioduct supports building multipart/form-data request bodies for file uploads and mixed form submissions.

Basic Usage

use aioduct::{TokioClient, Multipart};

#[tokio::main]
async fn main() -> Result<(), aioduct::Error> {
    let client = TokioClient::new();

    let form = Multipart::new()
        .text("username", "alice")
        .text("description", "Profile photo");

    let resp = client
        .post("http://example.com/upload")?
        .multipart(form)
        .send()
        .await?;

    println!("status: {}", resp.status());
    Ok(())
}

Text Fields

Add plain text form fields with .text(name, value):

#![allow(unused)]
fn main() {
use aioduct::Multipart;
let form = Multipart::new()
    .text("field1", "value1")
    .text("field2", "value2");
}

File Parts

Add file parts with .file(name, filename, content_type, data):

#![allow(unused)]
fn main() {
use aioduct::Multipart;
let form = Multipart::new()
    .text("description", "My document")
    .file("document", "report.pdf", "application/pdf", include_bytes!("../../Cargo.toml").as_slice());
}

The data parameter accepts anything that implements Into<Bytes>&[u8], Vec<u8>, String, Bytes, etc.

Mixed Forms

Combine text fields and file parts freely:

use aioduct::{TokioClient, Multipart};

#[tokio::main]
async fn main() -> Result<(), aioduct::Error> {
    let client = TokioClient::new();

    let image_data = std::fs::read("photo.jpg").unwrap();

    let form = Multipart::new()
        .text("title", "Vacation photo")
        .text("album", "Summer 2025")
        .file("photo", "photo.jpg", "image/jpeg", image_data);

    let resp = client
        .post("http://example.com/api/photos")?
        .multipart(form)
        .send()
        .await?;

    println!("uploaded: {}", resp.status());
    Ok(())
}

Wire Format

The generated body follows RFC 2046 multipart encoding:

------aioduct<boundary>\r\n
Content-Disposition: form-data; name="field1"\r\n
\r\n
value1\r\n
------aioduct<boundary>\r\n
Content-Disposition: form-data; name="file"; filename="photo.jpg"\r\n
Content-Type: image/jpeg\r\n
\r\n
<binary data>\r\n
------aioduct<boundary>--\r\n

The boundary is auto-generated per Multipart instance. The Content-Type header is set automatically when using .multipart() on the request builder.