feat: show download size on files read from disk
This commit is contained in:
parent
b8611e6415
commit
b44fa6c4a1
|
@ -43,13 +43,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.59"
|
version = "0.1.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
|
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.104",
|
"syn 2.0.22",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -135,6 +135,7 @@ dependencies = [
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"axum",
|
"axum",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"futures",
|
||||||
"hyper",
|
"hyper",
|
||||||
"log",
|
"log",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
@ -213,6 +214,21 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.28"
|
version = "0.3.28"
|
||||||
|
@ -220,6 +236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -228,6 +245,17 @@ version = "0.3.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.28"
|
version = "0.3.28"
|
||||||
|
@ -263,9 +291,13 @@ version = "0.3.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
|
|
|
@ -14,6 +14,7 @@ bytes = "1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
async-recursion = "1.0.0"
|
async-recursion = "1.0.0"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
futures = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
archived = { path = "./archived" }
|
archived = { path = "./archived" }
|
||||||
|
|
59
src/view.rs
59
src/view.rs
|
@ -10,8 +10,8 @@ use axum::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use hyper::StatusCode;
|
use hyper::{http::HeaderValue, StatusCode};
|
||||||
use tokio::fs::File;
|
use tokio::{fs::File, runtime::Handle};
|
||||||
use tokio_util::io::ReaderStream;
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
pub enum ViewSuccess {
|
pub enum ViewSuccess {
|
||||||
|
@ -20,7 +20,7 @@ pub enum ViewSuccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ViewError {
|
pub enum ViewError {
|
||||||
NotFound, // 404
|
NotFound, // 404
|
||||||
InternalServerError, // 500
|
InternalServerError, // 500
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,20 +28,67 @@ impl IntoResponse for ViewSuccess {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
match self {
|
match self {
|
||||||
ViewSuccess::FromDisk(file) => {
|
ViewSuccess::FromDisk(file) => {
|
||||||
|
// get handle to current runtime
|
||||||
|
// i use this to block on futures here (not async)
|
||||||
|
let handle = Handle::current();
|
||||||
|
let _ = handle.enter();
|
||||||
|
|
||||||
|
// read the metadata of the file on disk
|
||||||
|
// this function isn't async
|
||||||
|
// .. so we have to use handle.block_on() to get the metadata
|
||||||
|
let metadata = futures::executor::block_on(file.metadata());
|
||||||
|
|
||||||
|
// if we error then return 500
|
||||||
|
if metadata.is_err() {
|
||||||
|
error!("failed to read metadata from disk");
|
||||||
|
return ViewError::InternalServerError.into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap (which we know is safe) and read the file size as a string
|
||||||
|
let metadata = metadata.unwrap();
|
||||||
|
let len_str = metadata.len().to_string();
|
||||||
|
|
||||||
|
debug!("file is {} bytes on disk", &len_str);
|
||||||
|
|
||||||
|
// HeaderValue::from_str will never error if only visible ASCII characters are passed (32-127)
|
||||||
|
// .. so unwrapping this should be fine
|
||||||
|
let content_length = HeaderValue::from_str(&len_str).unwrap();
|
||||||
|
|
||||||
// create a streamed body response (we want to stream larger files)
|
// create a streamed body response (we want to stream larger files)
|
||||||
let reader = ReaderStream::new(file);
|
let reader = ReaderStream::new(file);
|
||||||
let stream = StreamBody::new(reader);
|
let stream = StreamBody::new(reader);
|
||||||
|
|
||||||
stream.into_response()
|
// extract mutable headers from the response
|
||||||
|
let mut res = stream.into_response();
|
||||||
|
let headers = res.headers_mut();
|
||||||
|
|
||||||
|
// clear headers, browser can imply content type
|
||||||
|
headers.clear();
|
||||||
|
|
||||||
|
// insert Content-Length header
|
||||||
|
// that way the browser shows how big a file is when it's being downloaded
|
||||||
|
headers.insert("Content-Length", content_length);
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
ViewSuccess::FromCache(data) => {
|
ViewSuccess::FromCache(data) => {
|
||||||
// extract mutable headers from the response
|
// extract mutable headers from the response
|
||||||
let mut res = data.into_response();
|
let mut res = data.clone().into_response();
|
||||||
let headers = res.headers_mut();
|
let headers = res.headers_mut();
|
||||||
|
|
||||||
// clear the headers, let the browser imply it
|
// clear the headers, let the browser imply it
|
||||||
headers.clear();
|
headers.clear();
|
||||||
|
|
||||||
|
/* // we do not need this for FromCache because it works fine
|
||||||
|
// read the length of the data as a string
|
||||||
|
let len_str = data.len().to_string();
|
||||||
|
|
||||||
|
// HeaderValue::from_str will never error if only visible ASCII characters are passed (32-127)
|
||||||
|
// .. so this should be fine
|
||||||
|
let content_length = HeaderValue::from_str(&len_str).unwrap();
|
||||||
|
headers.append("Content-Length", content_length);
|
||||||
|
*/
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +108,7 @@ impl IntoResponse for ViewError {
|
||||||
ViewError::InternalServerError => {
|
ViewError::InternalServerError => {
|
||||||
// convert string into response, change status code
|
// convert string into response, change status code
|
||||||
let mut res = "internal server error!".into_response();
|
let mut res = "internal server error!".into_response();
|
||||||
*res.status_mut() = StatusCode::NOT_FOUND;
|
*res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue