{"id":24725563,"url":"https://github.com/routerify/routerify-multipart","last_synced_at":"2025-07-31T14:15:19.826Z","repository":{"id":51264575,"uuid":"262816847","full_name":"routerify/routerify-multipart","owner":"routerify","description":"A multipart/form-data parser for Routerify","archived":false,"fork":false,"pushed_at":"2022-01-02T13:48:00.000Z","size":41,"stargazers_count":3,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-09T04:27:43.638Z","etag":null,"topics":["hyper-rs","multipart","multipart-form","multipart-formdata","routerify"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/routerify.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-10T15:26:59.000Z","updated_at":"2023-08-26T14:48:07.000Z","dependencies_parsed_at":"2022-09-12T22:00:48.314Z","dependency_job_id":null,"html_url":"https://github.com/routerify/routerify-multipart","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/routerify%2Frouterify-multipart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/routerify%2Frouterify-multipart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/routerify%2Frouterify-multipart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/routerify%2Frouterify-multipart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/routerify","download_url":"https://codeload.github.com/routerify/routerify-multipart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235854911,"owners_count":19055684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hyper-rs","multipart","multipart-form","multipart-formdata","routerify"],"created_at":"2025-01-27T13:19:47.982Z","updated_at":"2025-01-27T13:19:48.925Z","avatar_url":"https://github.com/routerify.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Github Actions Status](https://github.com/routerify/routerify-multipart/workflows/Test/badge.svg)](https://github.com/routerify/routerify-multipart/actions)\n[![crates.io](https://img.shields.io/crates/v/routerify-multipart.svg)](https://crates.io/crates/routerify-multipart)\n[![Documentation](https://docs.rs/routerify-multipart/badge.svg)](https://docs.rs/routerify-multipart)\n[![MIT](https://img.shields.io/crates/l/routerify-multipart.svg)](./LICENSE)\n\n# routerify-multipart\n\nA `multipart/form-data` parser for [`Routerify`](https://github.com/routerify/routerify).\n\nIt's using [multer](https://github.com/rousan/multer-rs) to parse the `multipart/form-data` content.\n\n[Docs](https://docs.rs/routerify-multipart)\n\n## Install\n\nAdd this to your `Cargo.toml` file:\n\n```toml\n[dependencies]\nrouterify = \"3\"\nrouterify-multipart = \"3\"\n```\n\n## Example\n\n```rust\nuse hyper::{Body, Request, Response, Server, StatusCode};\nuse routerify::{Error, Router, RouterService};\n// Import `RequestMultipartExt` trait.\nuse routerify_multipart::RequestMultipartExt;\nuse std::net::SocketAddr;\n\n// A handler to handle file uploading in `multipart/form-data` content-type.\nasync fn file_upload_handler(req: Request\u003cBody\u003e) -\u003e Result\u003cResponse\u003cBody\u003e, Error\u003e {\n    // Convert the request into a `Multipart` instance.\n    let mut multipart = match req.into_multipart() {\n        Ok(m) =\u003e m,\n        Err(err) =\u003e {\n            return Ok(Response::builder()\n                .status(StatusCode::BAD_REQUEST)\n                .body(Body::from(format!(\"Bad Request: {}\", err)))\n                .unwrap());\n        }\n    };\n\n    // Iterate over the fields.\n    while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {\n        // Get the field name.\n        let name = field.name();\n        // Get the field's filename if provided in \"Content-Disposition\" header.\n        let file_name = field.file_name();\n\n        println!(\"Name {:?}, File name: {:?}\", name, file_name);\n\n        // Process the field data chunks e.g. store them in a file.\n        while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {\n            // Do something with field chunk.\n            println!(\"Chunk: {:?}\", chunk);\n        }\n    }\n\n    Ok(Response::new(Body::from(\"Success\")))\n}\n\n// Create a router.\nfn router() -\u003e Router\u003cBody, Error\u003e {\n    // Register the handlers.\n    Router::builder().post(\"/upload\", file_upload_handler).build().unwrap()\n}\n\n#[tokio::main]\nasync fn main() {\n    let router = router();\n\n    // Create a Service from the router above to handle incoming requests.\n    let service = RouterService::new(router).unwrap();\n\n    // The address on which the server will be listening.\n    let addr = SocketAddr::from(([127, 0, 0, 1], 3001));\n\n    // Create a server by passing the created service to `.serve` method.\n    let server = Server::bind(\u0026addr).serve(service);\n\n    println!(\"App is running on: {}\", addr);\n    if let Err(err) = server.await {\n        eprintln!(\"Server error: {}\", err);\n    }\n}\n``` \n\n## Prevent DDoS Attack\nThis crate also provides some APIs to prevent potential `DDoS attack` with fine grained control. It's recommended to add some constraints\non field (specially text field) size to avoid potential `DDoS attack` from attackers running the server out of memory.\n\nAn example:\n\n```rust\nuse hyper::{Body, Request, Response, Server, StatusCode};\nuse routerify::{Error, Router, RouterService};\n// Import `RequestMultipartExt` trait and other types.\nuse routerify_multipart::{RequestMultipartExt, Constraints, SizeLimit};\nuse std::net::SocketAddr;\n\n// A handler to handle file uploading in `multipart/form-data` content-type.\nasync fn file_upload_handler(req: Request\u003cBody\u003e) -\u003e Result\u003cResponse\u003cBody\u003e, Error\u003e {\n    // Create some constraints to be applied to the fields to prevent DDoS attack.\n     let constraints = Constraints::new()\n         // We only accept `my_text_field` and `my_file_field` fields,\n         // For any unknown field, we will throw an error.\n         .allowed_fields(vec![\"my_text_field\", \"my_file_field\"])\n         .size_limit(\n             SizeLimit::new()\n                 // Set 15mb as size limit for the whole stream body.\n                 .whole_stream(15 * 1024 * 1024)\n                 // Set 10mb as size limit for all fields.\n                 .per_field(10 * 1024 * 1024)\n                 // Set 30kb as size limit for our text field only.\n                 .for_field(\"my_text_field\", 30 * 1024),\n          );\n\n    // Convert the request into a `Multipart` instance.\n    let mut multipart = match req.into_multipart_with_constraints(constraints) {\n        Ok(m) =\u003e m,\n        Err(err) =\u003e {\n            return Ok(Response::builder()\n                .status(StatusCode::BAD_REQUEST)\n                .body(Body::from(format!(\"Bad Request: {}\", err)))\n                .unwrap());\n        }\n    };\n\n    // Iterate over the fields.\n    while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? {\n        // Get the field name.\n        let name = field.name();\n        // Get the field's filename if provided in \"Content-Disposition\" header.\n        let file_name = field.file_name();\n\n        println!(\"Name {:?}, File name: {:?}\", name, file_name);\n\n        // Process the field data chunks e.g. store them in a file.\n        while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? {\n            // Do something with field chunk.\n            println!(\"Chunk: {:?}\", chunk);\n        }\n    }\n\n    Ok(Response::new(Body::from(\"Success\")))\n}\n```\n\n## Contributing\n\nYour PRs and suggestions are always welcome.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frouterify%2Frouterify-multipart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frouterify%2Frouterify-multipart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frouterify%2Frouterify-multipart/lists"}