From d00a7ef8dcc8c3a54caf7b3e47f8029e63eb183e Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Fri, 10 May 2024 17:23:23 +0200 Subject: [PATCH] stream books instead of reading them into memory --- Cargo.lock | 1 + rusty-library/Cargo.toml | 1 + rusty-library/src/handlers/download.rs | 27 +++++++++++++++----------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f9b5bd..defc78b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1371,6 +1371,7 @@ dependencies = [ "thiserror", "time", "tokio", + "tokio-util", "tracing", "tracing-subscriber", "uuid", diff --git a/rusty-library/Cargo.toml b/rusty-library/Cargo.toml index dfa2399..de77660 100644 --- a/rusty-library/Cargo.toml +++ b/rusty-library/Cargo.toml @@ -17,6 +17,7 @@ tera = "1.19.1" thiserror = { workspace = true } time = { workspace = true } tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros"] } +tokio-util = "0.7.11" tracing = "0.1.40" tracing-subscriber = "0.3.18" uuid = { version = "1.8.0", features = ["v4", "fast-rng"] } diff --git a/rusty-library/src/handlers/download.rs b/rusty-library/src/handlers/download.rs index 6b203c4..1f25c56 100644 --- a/rusty-library/src/handlers/download.rs +++ b/rusty-library/src/handlers/download.rs @@ -1,18 +1,22 @@ //! Handle requests for specific formats of a book. -use std::{fs::File, io::Read, sync::Arc}; +use std::sync::Arc; + +use tokio::fs::File; use poem::{ error::NotFoundError, handler, - web::{Data, Path, WithContentType, WithHeader}, - IntoResponse, + web::{Data, Path}, + Body, IntoResponse, Response, }; +use tokio_util::io::ReaderStream; use crate::{ app_state::AppState, data::book::{Book, Format}, handlers::error::HandlerError, + opds::media_type::MediaType, }; /// Handle a request for a book with id `id` in format `format`. @@ -20,7 +24,7 @@ use crate::{ pub async fn handler( Path((id, format)): Path<(u64, String)>, state: Data<&Arc>, -) -> Result>>, poem::Error> { +) -> Result { let book = state .calibre .scalar_book(id) @@ -33,13 +37,14 @@ pub async fn handler( .library_path .join(book.data.path) .join(file_name); - let mut file = File::open(file_path).map_err(|_| NotFoundError)?; - let mut data = Vec::new(); - file.read_to_end(&mut data).map_err(|_| NotFoundError)?; - let content_type = format.0; + let mut file = File::open(file_path).await.map_err(|_| NotFoundError)?; + let stream = ReaderStream::new(file); + let body = Body::from_bytes_stream(stream); - Ok(data - .with_content_type(content_type) - .with_header("Content-Disposition", format!("filename={file_name};"))) + let content_type: MediaType = format.into(); + Ok(body + .with_content_type(format!("{content_type}")) + .with_header("Content-Disposition", format!("filename=\"{file_name}\"")) + .into_response()) }