stream books instead of reading them into memory

This commit is contained in:
Sebastian Hugentobler 2024-05-10 17:23:23 +02:00
parent d7f056f77e
commit d00a7ef8dc
Signed by: shu
GPG Key ID: BB32CF3CA052C2F0
3 changed files with 18 additions and 11 deletions

1
Cargo.lock generated
View File

@ -1371,6 +1371,7 @@ dependencies = [
"thiserror",
"time",
"tokio",
"tokio-util",
"tracing",
"tracing-subscriber",
"uuid",

View File

@ -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"] }

View File

@ -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<AppState>>,
) -> Result<WithHeader<WithContentType<Vec<u8>>>, poem::Error> {
) -> Result<Response, poem::Error> {
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())
}