From f5fce67a33e876ff773c7b2f14947590b74b6630 Mon Sep 17 00:00:00 2001 From: Bulat Kurbanov Date: Thu, 19 Jan 2023 16:36:46 +0100 Subject: [PATCH] Add x-filename-b64 --- Cargo.lock | 1 + Cargo.toml | 1 + src/services/downloader/mod.rs | 52 ++++++++++++++++++++++++-------- src/services/downloader/types.rs | 5 +-- src/services/filename_getter.rs | 10 +++--- src/views.rs | 10 ++++-- 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 749ec54..6623290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,7 @@ version = "0.1.0" dependencies = [ "axum", "axum-prometheus", + "base64", "bytes", "env_logger", "futures", diff --git a/Cargo.toml b/Cargo.toml index a69f867..794efd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ zip = "0.6.3" tempfile = "3.3.0" bytes = "1.3.0" axum-prometheus = "0.2.0" +base64 = "0.13.0" diff --git a/src/services/downloader/mod.rs b/src/services/downloader/mod.rs index 8607928..08b49bb 100644 --- a/src/services/downloader/mod.rs +++ b/src/services/downloader/mod.rs @@ -94,13 +94,27 @@ pub async fn download_chain<'a>( }; if is_zip && book.file_type.to_lowercase() == "html" { - let filename = get_filename_by_book(book, file_type, true); - return Some(DownloadResult::new(Data::Response(response), filename)); + let filename = get_filename_by_book(book, file_type, true, false); + let filename_ascii = get_filename_by_book(book, file_type, true, true); + return Some( + DownloadResult::new( + Data::Response(response), + filename, + filename_ascii + ) + ); } if !is_zip && !final_need_zip && !converting { - let filename = get_filename_by_book(book, &book.file_type, false); - return Some(DownloadResult::new(Data::Response(response), filename)); + let filename = get_filename_by_book(book, &book.file_type, false, false); + let filename_ascii = get_filename_by_book(book, file_type, false, true); + return Some( + DownloadResult::new( + Data::Response(response), + filename, + filename_ascii, + ) + ); }; let unziped_temp_file = { @@ -133,17 +147,31 @@ pub async fn download_chain<'a>( if !final_need_zip { let t = SpooledTempAsyncRead::new(clean_file); - let filename = get_filename_by_book(book, file_type, false); - return Some(DownloadResult::new(Data::SpooledTempAsyncRead(t), filename)); + let filename = get_filename_by_book(book, file_type, false, false); + let filename_ascii = get_filename_by_book(book, file_type, false, true); + return Some( + DownloadResult::new( + Data::SpooledTempAsyncRead(t), + filename, + filename_ascii + ) + ); }; let t_file_type = if file_type == "fb2zip" { "fb2" } else { file_type }; - let filename = get_filename_by_book(book, t_file_type, false); + let filename = get_filename_by_book(book, t_file_type, false, false); match zip(&mut clean_file, filename.as_str()) { Some(v) => { let t = SpooledTempAsyncRead::new(v); - let filename = get_filename_by_book(book, file_type, true); - Some(DownloadResult::new(Data::SpooledTempAsyncRead(t), filename)) + let filename = get_filename_by_book(book, file_type, true, false); + let filename_ascii = get_filename_by_book(book, file_type, true, true); + Some( + DownloadResult::new( + Data::SpooledTempAsyncRead(t), + filename, + filename_ascii + ) + ) }, None => None, } @@ -187,16 +215,14 @@ pub async fn book_download( source_id: u32, remote_id: u32, file_type: &str, -) -> Result, Box> { +) -> Result, Box> { let book = match get_remote_book(source_id, remote_id).await { Ok(v) => v, Err(err) => return Err(err), }; - let filename = get_filename_by_book(&book, file_type, false); - match start_download_futures(&book, file_type).await { - Some(v) => Ok(Some((v, filename))), + Some(v) => Ok(Some(v)), None => Ok(None), } } diff --git a/src/services/downloader/types.rs b/src/services/downloader/types.rs index 487c4ac..c51186d 100644 --- a/src/services/downloader/types.rs +++ b/src/services/downloader/types.rs @@ -14,6 +14,7 @@ pub enum Data { pub struct DownloadResult { pub data: Data, pub filename: String, + pub filename_ascii: String, } pub fn get_response_async_read(it: Response) -> impl AsyncRead { @@ -24,8 +25,8 @@ pub fn get_response_async_read(it: Response) -> impl AsyncRead { } impl DownloadResult { - pub fn new(data: Data, filename: String) -> Self { - Self { data, filename } + pub fn new(data: Data, filename: String, filename_ascii: String) -> Self { + Self { data, filename, filename_ascii } } pub fn get_async_read(self) -> Pin> { diff --git a/src/services/filename_getter.rs b/src/services/filename_getter.rs index e42c97e..4e3ca9b 100644 --- a/src/services/filename_getter.rs +++ b/src/services/filename_getter.rs @@ -22,7 +22,7 @@ pub fn get_author_short_name(author: BookAuthor) -> String { parts.join(" ") } -pub fn get_filename_by_book(book: &BookWithRemote, file_type: &str, force_zip: bool) -> String { +pub fn get_filename_by_book(book: &BookWithRemote, file_type: &str, force_zip: bool, only_ascii: bool) -> String { let book_id = book.remote_id; let mut filename_parts: Vec = vec![]; @@ -73,12 +73,14 @@ pub fn get_filename_by_book(book: &BookWithRemote, file_type: &str, force_zip: b .collect(); let replace_transliterator = Transliterator::new(replace_char_map); - let normal_filename = replace_transliterator.convert(&filename_without_type, false); + let mut normal_filename = replace_transliterator.convert(&filename_without_type, false); - let normal_filename = normal_filename.replace(|c: char| !c.is_ascii(), ""); + if only_ascii { + normal_filename = normal_filename.replace(|c: char| !c.is_ascii(), ""); + } let right_part = format!(".{book_id}.{file_type_}"); - let normal_filename_slice = std::cmp::min(64 - right_part.len() - 1, normal_filename.len()); + let normal_filename_slice = std::cmp::min(64 - right_part.len() - 1, normal_filename.len() -1); let left_part = normal_filename.get(..normal_filename_slice).expect( &format!("Can't slice left part: {:?} {:?}", normal_filename, normal_filename_slice) ); diff --git a/src/views.rs b/src/views.rs index 5b04bc5..2a92708 100644 --- a/src/views.rs +++ b/src/views.rs @@ -30,17 +30,21 @@ pub async fn download( }, }; - let (data, filename) = match download_result { + let data = match download_result { Some(v) => v, None => return Err((StatusCode::NO_CONTENT, "Can't download!".to_string())), }; + let filename = data.filename.clone(); + let filename_ascii = data.filename_ascii.clone(); + let reader = data.get_async_read(); let stream = ReaderStream::new(reader); let body = StreamBody::new(stream); let headers = AppendHeaders([ - (header::CONTENT_DISPOSITION, format!("attachment; filename={filename}")) + (header::CONTENT_DISPOSITION, format!("attachment; filename={filename_ascii}")), + (header::HeaderName::from_static("x-filename-b64"), base64::encode(filename)) ]); Ok((headers, body)) @@ -62,7 +66,7 @@ pub async fn get_filename( } let filename = match get_book(book_id).await { - Ok(book) => get_filename_by_book(&book, file_type.as_str(), false), + Ok(book) => get_filename_by_book(&book, file_type.as_str(), false, false), Err(_) => return (StatusCode::BAD_REQUEST, "Book not found!".to_string()), };