create a http server and upload a file and return a json response in rust

Here's an example code snippet to create an HTTP server in Rust using the Hyper framework to handle file uploads and return a JSON response:

main.rs
use std::env;
use std::io::copy;
use std::fs::{self, File};
use std::path::PathBuf;
use std::sync::Arc;

use tokio::fs::OpenOptions;
use tokio::sync::Mutex;
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use hyper::header::{HeaderValue, CONTENT_TYPE};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct FileResponse {
    success: bool,
    message: String,
}

// Handler for file uploads
async fn handle_file_upload(req: Request<Body>, file_path: Arc<Mutex<PathBuf>>) -> Result<Response<Body>, hyper::Error> {
    match req.method() {
        &Method::POST => {
            let content_type_header = req
                .headers()
                .get(CONTENT_TYPE)
                .ok_or_else(|| {
                    Response::builder()
                        .status(StatusCode::BAD_REQUEST)
                        .body(Body::empty())
                        .unwrap_err()
                })?;

            // Check if the Content-Type header indicates a form submission.
            if !content_type_header
                .to_str()
                .map(|v| v.contains("multipart/form-data"))
                .unwrap_or(false)
            {
                return Ok(Response::builder()
                    .status(StatusCode::BAD_REQUEST)
                    .body(Body::empty())
                    .unwrap());
            }

            let mut multipart = match req.multipart() {
                Ok(m) => m,
                Err(_) => {
                    return Ok(Response::builder()
                        .status(StatusCode::BAD_REQUEST)
                        .body(Body::empty())
                        .unwrap());
                }
            };
            let mut file_name = String::new();

            // Process each part of the multipart form data
            while let Some(part) = multipart.next_field().await.unwrap() {
                let content_disp = part.headers().get("content-disposition").unwrap().to_str().unwrap();
                let filename = content_disp.split("filename=").nth(1).unwrap().replace("\"", "");

                // Create a file with the provided name
                let mut file = OpenOptions::new()
                    .write(true)
                    .create(true)
                    .truncate(true)
                    .open(&format!("./{}", filename))
                    .await.unwrap();

                let mut field_str = String::new();

                // Read the content of the part and write it to the file
                while let Some(chunk) = part.chunk().await.unwrap() {
                    field_str.push_str(&String::from_utf8_lossy(&chunk));
                    file.write_all(&chunk).await.unwrap();
                }

                // Save the name of the file for later reference
                file_name = filename;
            }

            let mut file_path = file_path.lock().await;
            file_path.push(&file_name);

            let response = FileResponse {
                success: true,
                message: format!("File {} uploaded successfully!", &file_name),
            };
            let body = serde_json::to_string(&response).unwrap();

            Ok(Response::builder()
                .header(CONTENT_TYPE, "text/json")
                .body(Body::from(body))
                .unwrap())
        },
        _ => Ok(Response::builder()
            .status(StatusCode::METHOD_NOT_ALLOWED)
            .body(Body::empty())
            .unwrap())
    }
}

#[tokio::main]
async fn main() {
    let file_path = Arc::new(Mutex::new(PathBuf::from(".")));

    let addr = env::var("LISTEN_ADDR").unwrap_or_else(|_| "127.0.0.1:8080".into());
    let addr = addr.parse().unwrap();
    let make_svc = hyper::service::make_service_fn(|_conn| {
        let file_path = file_path.clone();
        async {
            Ok::<_, hyper::Error>(hyper::service::service_fn(move |req| handle_file_upload(req, file_path.clone())))
        }
    });

    let server = Server::bind(&addr).serve(make_svc);
    println!("Listening on http://{}", addr);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}
4178 chars
122 lines

This example creates a server that listens for file uploads via a multipart form-data POST request. Once uploaded, the file is saved in the current directory and a JSON response is returned indicating whether the upload was successful.

gistlibby LogSnag