upload.rs
1 use core::fmt::Write; 2 3 use alloc::string::String as AllocString; 4 use embassy_time::Duration; 5 use heapless::String as HeaplessString; 6 use picoserve::response::{IntoResponse, StatusCode}; 7 8 use crate::filesystems::sd::is_supported_flat_file_name; 9 10 const CHUNK_SIZE: usize = 4096; 11 12 pub struct FileUploadService; 13 14 impl picoserve::routing::RequestHandlerService<(), (AllocString,)> for FileUploadService { 15 async fn call_request_handler_service< 16 R: picoserve::io::Read, 17 W: picoserve::response::ResponseWriter<Error = R::Error>, 18 >( 19 &self, 20 _state: &(), 21 (file_name,): (AllocString,), 22 mut request: picoserve::request::Request<'_, R>, 23 response_writer: W, 24 ) -> Result<picoserve::ResponseSent, W::Error> { 25 if !is_supported_flat_file_name(&file_name) { 26 return (StatusCode::BAD_REQUEST, "invalid path\n") 27 .write_to(request.body_connection.finalize().await?, response_writer) 28 .await; 29 } 30 31 use picoserve::io::Read; 32 33 let content_length = request.body_connection.body().content_length(); 34 let timeout_secs = (content_length / 10_000).max(60) as u64; 35 let timeout = Duration::from_secs(timeout_secs); 36 37 let mut reader = request 38 .body_connection 39 .body() 40 .reader() 41 .with_different_timeout(timeout); 42 43 let mut chunk = [0u8; CHUNK_SIZE]; 44 let mut offset = 0u32; 45 46 loop { 47 let mut filled = 0; 48 while filled < CHUNK_SIZE { 49 let bytes_read = reader.read(&mut chunk[filled..]).await?; 50 if bytes_read == 0 { 51 break; 52 } 53 filled += bytes_read; 54 } 55 56 if filled == 0 { 57 break; 58 } 59 60 if let Err(error_message) = 61 crate::filesystems::sd::write_file_chunk(&file_name, offset, &chunk[..filled]) 62 { 63 let mut error_response = HeaplessString::<128>::new(); 64 let _ = write!( 65 error_response, 66 "write failed at byte {}: {}\n", 67 offset, 68 error_message 69 ); 70 return (StatusCode::INTERNAL_SERVER_ERROR, error_response) 71 .write_to(request.body_connection.finalize().await?, response_writer) 72 .await; 73 } 74 offset += filled as u32; 75 } 76 77 let mut success_response = HeaplessString::<64>::new(); 78 let _ = write!(success_response, "ok {} bytes\n", offset); 79 (StatusCode::OK, success_response) 80 .write_to(request.body_connection.finalize().await?, response_writer) 81 .await 82 } 83 }