@@ -9,12 +9,15 @@ use std::path::{Component, Path, PathBuf};
99use anyhow:: { Context , Result } ;
1010use async_trait:: async_trait;
1111use bytes:: Bytes ;
12+ use futures:: StreamExt ;
13+ use http:: HeaderName ;
1214use http:: { HeaderValue , Method , Response , StatusCode , Uri , header:: CONTENT_TYPE , request:: Parts } ;
1315use http_body_util:: { BodyExt , Full } ;
14- use multer :: Multipart ;
16+ use hyper :: body :: Incoming ;
1517use percent_encoding:: { percent_decode_str, utf8_percent_encode} ;
1618use proto:: { DirectoryEntry , DirectoryIndex , EntryType , Sort } ;
1719use rust_embed:: Embed ;
20+ use tokio:: fs:: File ;
1821use tokio:: io:: AsyncWriteExt ;
1922
2023use crate :: handler:: Handler ;
@@ -23,6 +26,9 @@ use crate::server::{HttpRequest, HttpResponse};
2326use self :: proto:: BreadcrumbItem ;
2427use self :: utils:: { PERCENT_ENCODE_SET , decode_uri, encode_uri} ;
2528
29+ const X_FILE_NAME : & str = "x-file-name" ;
30+ const X_FILE_NAME_HTTP_HEADER : HeaderName = HeaderName :: from_static ( X_FILE_NAME ) ;
31+
2632#[ derive( Embed ) ]
2733#[ folder = "./ui" ]
2834struct FileExplorerAssets ;
@@ -40,8 +46,8 @@ impl FileExplorer {
4046 }
4147 }
4248
43- async fn handle_api ( & self , parts : Parts , body : Bytes ) -> Result < HttpResponse > {
44- let path = Self :: parse_req_uri ( parts. uri . clone ( ) ) . unwrap ( ) ;
49+ async fn handle_api ( & self , parts : Parts , body : Incoming ) -> Result < HttpResponse > {
50+ let path = Self :: parse_req_uri ( parts. uri . clone ( ) ) ? ;
4551
4652 match parts. method {
4753 Method :: GET => match self . file_explorer . peek ( path) . await {
@@ -75,34 +81,13 @@ impl FileExplorer {
7581 Ok ( Response :: new ( Full :: new ( Bytes :: from ( message) ) ) )
7682 }
7783 } ,
78- Method :: POST => {
79- self . handle_file_upload ( parts, body) . await ?;
80- Ok ( Response :: new ( Full :: new ( Bytes :: from (
81- "POST method is not supported" ,
82- ) ) ) )
83- }
84+ Method :: POST => self . handle_file_upload ( parts, body) . await ,
8485 _ => Ok ( Response :: new ( Full :: new ( Bytes :: from ( "Unsupported method" ) ) ) ) ,
8586 }
8687 }
8788
88- async fn handle_file_upload ( & self , parts : Parts , body : Bytes ) -> Result < HttpResponse > {
89- // Extract the `multipart/form-data` boundary from the headers.
90- let mb_boundary = parts
91- . headers
92- . get ( CONTENT_TYPE )
93- . and_then ( |ct| ct. to_str ( ) . ok ( ) )
94- . and_then ( |ct| multer:: parse_boundary ( ct) . ok ( ) ) ;
95-
96- // Send `BAD_REQUEST` status if the content-type is not multipart/form-data.
97- let Some ( boundary) = mb_boundary else {
98- return Ok ( Response :: builder ( )
99- . status ( StatusCode :: BAD_REQUEST )
100- . body ( Full :: from ( "BAD REQUEST" ) )
101- . unwrap ( ) ) ;
102- } ;
103-
104- // Process the multipart e.g. you can store them in files.
105- if let Err ( err) = self . process_multipart ( body, boundary) . await {
89+ async fn handle_file_upload ( & self , parts : Parts , body : Incoming ) -> Result < HttpResponse > {
90+ if let Err ( err) = self . process_multipart ( body, parts) . await {
10691 return Ok ( Response :: builder ( )
10792 . status ( StatusCode :: INTERNAL_SERVER_ERROR )
10893 . body ( Full :: from ( format ! ( "INTERNAL SERVER ERROR: {err}" ) ) )
@@ -112,31 +97,21 @@ impl FileExplorer {
11297 Ok ( Response :: new ( Full :: from ( "Success" ) ) )
11398 }
11499
115- async fn process_multipart ( & self , bytes : Bytes , boundary : String ) -> Result < ( ) > {
116- let cursor = std :: io :: Cursor :: new ( bytes ) ;
117- let bytes_stream = tokio_util :: io :: ReaderStream :: new ( cursor ) ;
118- let mut multipart = Multipart :: new ( bytes_stream , boundary ) ;
119-
120- while let Some ( mut field ) = multipart . next_field ( ) . await ? {
121- let name = field . name ( ) ;
122- let file_name = field
123- . file_name ( )
124- . to_owned ( )
125- . context ( "No file name available in form file." ) ? ;
126- let content_type = field . content_type ( ) ;
127- let mut file = tokio :: fs :: File :: create ( file_name )
100+ async fn process_multipart ( & self , bytes : Incoming , parts : Parts ) -> Result < ( ) > {
101+ let file_name = parts
102+ . headers
103+ . get ( X_FILE_NAME_HTTP_HEADER )
104+ . and_then ( |hv| hv . to_str ( ) . ok ( ) )
105+ . context ( format ! ( "Missing '{X_FILE_NAME}' header" ) ) ? ;
106+ let mut stream = bytes . into_data_stream ( ) ;
107+ let mut file = File :: create ( file_name )
108+ . await
109+ . context ( "Failed to create target file for upload." ) ? ;
110+
111+ while let Some ( Ok ( bytes ) ) = stream . next ( ) . await {
112+ file. write_all ( & bytes )
128113 . await
129- . context ( "Failed to create target file for upload." ) ?;
130-
131- println ! (
132- "\n \n Name: {name:?}, FileName: {file_name:?}, Content-Type: {content_type:?}\n \n "
133- ) ;
134-
135- while let Some ( field_chunk) = field. chunk ( ) . await ? {
136- file. write_all ( & field_chunk)
137- . await
138- . context ( "Failed to write bytes to file" ) ?;
139- }
114+ . context ( "Failed to write bytes to file" ) ?;
140115 }
141116
142117 Ok ( ( ) )
@@ -293,7 +268,6 @@ impl FileExplorer {
293268impl Handler for FileExplorer {
294269 async fn handle ( & self , req : HttpRequest ) -> Result < HttpResponse > {
295270 let ( parts, body) = req. into_parts ( ) ;
296- let body = body. collect ( ) . await . unwrap ( ) . to_bytes ( ) ;
297271
298272 if parts. uri . path ( ) . starts_with ( "/api/v1" ) {
299273 return self . handle_api ( parts, body) . await ;
0 commit comments