|
| 1 | +use hyper::{Body, Request, Response, Server, StatusCode}; |
| 2 | +use routerify::{Error, Router, RouterService}; |
| 3 | +// Import `RequestMultipartExt` trait and other types. |
| 4 | +use routerify_multipart::{Constraints, RequestMultipartExt, SizeLimit}; |
| 5 | +use std::net::SocketAddr; |
| 6 | + |
| 7 | +// A handler to handle file uploading in `multipart/form-data` content-type. |
| 8 | +async fn file_upload_handler(req: Request<Body>) -> Result<Response<Body>, Error> { |
| 9 | + // Create some constraints to be applied to the fields to prevent DDoS attack. |
| 10 | + let constraints = Constraints::new() |
| 11 | + // We only accept `my_text_field` and `my_file_field` fields, |
| 12 | + // For any unknown field, we will throw an error. |
| 13 | + .allowed_fields(vec!["my_text_field", "my_file_field"]) |
| 14 | + .size_limit( |
| 15 | + SizeLimit::new() |
| 16 | + // Set 15mb as size limit for the whole stream body. |
| 17 | + .whole_stream(15 * 1024 * 1024) |
| 18 | + // Set 10mb as size limit for all fields. |
| 19 | + .per_field(10 * 1024 * 1024) |
| 20 | + // Set 30kb as size limit for our text field only. |
| 21 | + .for_field("my_text_field", 30 * 1024), |
| 22 | + ); |
| 23 | + |
| 24 | + // Convert the request into a `Multipart` instance. |
| 25 | + let mut multipart = match req.into_multipart_with_constraints(constraints) { |
| 26 | + Ok(m) => m, |
| 27 | + Err(err) => { |
| 28 | + return Ok(Response::builder() |
| 29 | + .status(StatusCode::BAD_REQUEST) |
| 30 | + .body(Body::from(format!("Bad Request: {}", err))) |
| 31 | + .unwrap()); |
| 32 | + } |
| 33 | + }; |
| 34 | + |
| 35 | + // Iterate over the fields. |
| 36 | + while let Some(mut field) = multipart.next_field().await.map_err(|err| Error::wrap(err))? { |
| 37 | + // Get the field name. |
| 38 | + let name = field.name(); |
| 39 | + // Get the field's filename if provided in "Content-Disposition" header. |
| 40 | + let file_name = field.file_name(); |
| 41 | + |
| 42 | + println!("Name {:?}, File name: {:?}", name, file_name); |
| 43 | + |
| 44 | + // Process the field data chunks e.g. store them in a file. |
| 45 | + while let Some(chunk) = field.chunk().await.map_err(|err| Error::wrap(err))? { |
| 46 | + // Do something with field chunk. |
| 47 | + println!("Chunk: {:?}", chunk); |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + Ok(Response::new(Body::from("Success"))) |
| 52 | +} |
| 53 | + |
| 54 | +// Create a router. |
| 55 | +fn router() -> Router<Body, Error> { |
| 56 | + // Register the handlers. |
| 57 | + Router::builder().post("/upload", file_upload_handler).build().unwrap() |
| 58 | +} |
| 59 | + |
| 60 | +#[tokio::main] |
| 61 | +async fn main() { |
| 62 | + let router = router(); |
| 63 | + |
| 64 | + // Create a Service from the router above to handle incoming requests. |
| 65 | + let service = RouterService::new(router).unwrap(); |
| 66 | + |
| 67 | + // The address on which the server will be listening. |
| 68 | + let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); |
| 69 | + |
| 70 | + // Create a server by passing the created service to `.serve` method. |
| 71 | + let server = Server::bind(&addr).serve(service); |
| 72 | + |
| 73 | + println!("App is running on: {}", addr); |
| 74 | + if let Err(err) = server.await { |
| 75 | + eprintln!("Server error: {}", err); |
| 76 | + } |
| 77 | +} |
0 commit comments