leave.rs
1 use crate::base::kit::{DIRECTORY, path_is_valid}; 2 use axum::{ 3 BoxError, 4 body::Bytes, 5 extract::{Multipart, Path, Request}, 6 http::StatusCode, 7 response::Redirect, 8 }; 9 use futures::{Stream, TryStreamExt}; 10 use std::io; 11 12 use tokio::{fs::File, io::BufWriter}; 13 14 use tokio_util::io::StreamReader; 15 16 pub async fn save_request_body( 17 Path(file_name): Path<String>, 18 request: Request, 19 ) -> Result<(), (StatusCode, String)> { 20 stream_to_item(&file_name, request.into_body().into_data_stream()).await 21 } 22 23 pub async fn accept_items( 24 mut multipart: Multipart, 25 ) -> Result<Redirect, (StatusCode, String)> { 26 while let Ok(Some(field)) = multipart.next_field().await { 27 let file_name = if let Some(file_name) = field.file_name() { 28 file_name.to_owned() 29 } else { 30 continue; 31 }; 32 33 stream_to_item(&file_name, field).await?; 34 } 35 36 Ok(Redirect::to("/done")) 37 } 38 39 async fn stream_to_item<S, E>( 40 path: &str, 41 stream: S, 42 ) -> Result<(), (StatusCode, String)> 43 where 44 S: Stream<Item = Result<Bytes, E>>, 45 E: Into<BoxError>, 46 { 47 if !path_is_valid(path) { 48 return Err((StatusCode::BAD_REQUEST, "Invalid path".to_owned())); 49 } 50 51 async { 52 let body_with_io_error = stream.map_err(|err| io::Error::other(err)); 53 let body_reader = StreamReader::new(body_with_io_error); 54 futures::pin_mut!(body_reader); 55 56 let path = std::path::Path::new(DIRECTORY).join(path); 57 let mut file = BufWriter::new(File::create(path).await?); 58 59 tokio::io::copy(&mut body_reader, &mut file).await?; 60 tracing::info!("File uploaded"); 61 Ok::<_, io::Error>(()) 62 } 63 .await 64 .map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string())) 65 }