forked from neri/datatrash
prefer to serve raw files over html files
This commit is contained in:
parent
30d059b7af
commit
48574b1ec3
|
@ -3,11 +3,14 @@ use std::str::FromStr;
|
|||
use actix_files::NamedFile;
|
||||
use actix_web::{
|
||||
error,
|
||||
http::header::{Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue},
|
||||
http::header::{
|
||||
Accept, Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue,
|
||||
Header,
|
||||
},
|
||||
web, Error, HttpRequest, HttpResponse,
|
||||
};
|
||||
use async_std::{fs, path::Path};
|
||||
use mime::Mime;
|
||||
use mime::{Mime, TEXT_HTML};
|
||||
use sqlx::postgres::PgPool;
|
||||
use url::Url;
|
||||
|
||||
|
@ -19,24 +22,29 @@ const URL_VIEW_HTML: &str = include_str!("../template/url-view.html");
|
|||
|
||||
const TEXT_VIEW_SIZE_LIMIT: u64 = 512 * 1024; // 512KiB
|
||||
|
||||
enum ViewType {
|
||||
Raw,
|
||||
Download,
|
||||
Html,
|
||||
}
|
||||
|
||||
pub async fn download(
|
||||
req: HttpRequest,
|
||||
db: web::Data<PgPool>,
|
||||
config: web::Data<Config>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let id = req.match_info().query("id");
|
||||
let (file_id, file_name, file_kind, delete_on_download) = load_file_info(id, &db).await?;
|
||||
let (file_id, file_name, file_kind, delete) = load_file_info(id, &db).await?;
|
||||
let mut path = config.files_dir.clone();
|
||||
path.push(&file_id);
|
||||
|
||||
let download = delete_on_download || req.query_string().contains("dl");
|
||||
let content_type = get_content_type(&path);
|
||||
let response = if use_text_view(&file_kind, &content_type, &path, download).await {
|
||||
build_text_response(&path).await
|
||||
} else {
|
||||
build_file_response(download, &file_name, path, content_type, req)
|
||||
let file_mime = get_content_type(&path);
|
||||
let response = match get_view_type(&req, &file_kind, &file_mime, &path, delete).await {
|
||||
ViewType::Raw => build_file_response(false, &file_name, path, file_mime, &req),
|
||||
ViewType::Download => build_file_response(true, &file_name, path, file_mime, &req),
|
||||
ViewType::Html => build_text_response(&path).await,
|
||||
};
|
||||
if delete_on_download {
|
||||
if delete {
|
||||
deleter::delete_by_id(&db, &file_id, &config.files_dir)
|
||||
.await
|
||||
.map_err(|db_err| {
|
||||
|
@ -72,16 +80,44 @@ fn get_content_type(path: &Path) -> Mime {
|
|||
.expect("tree_magic_mini should not produce invalid mime")
|
||||
}
|
||||
|
||||
async fn use_text_view(
|
||||
async fn get_view_type(
|
||||
req: &HttpRequest,
|
||||
file_kind: &str,
|
||||
content_type: &Mime,
|
||||
file_mime: &Mime,
|
||||
file_path: &Path,
|
||||
download: bool,
|
||||
) -> bool {
|
||||
delete_on_download: bool,
|
||||
) -> ViewType {
|
||||
if delete_on_download || req.query_string().contains("dl") {
|
||||
return ViewType::Download;
|
||||
}
|
||||
let is_text =
|
||||
FileKind::from_str(file_kind) == Ok(FileKind::Text) || content_type.type_() == mime::TEXT;
|
||||
let is_not_large = get_file_size(file_path).await < TEXT_VIEW_SIZE_LIMIT;
|
||||
is_text && is_not_large && !download
|
||||
FileKind::from_str(file_kind) == Ok(FileKind::Text) || file_mime.type_() == mime::TEXT;
|
||||
if !is_text {
|
||||
return ViewType::Raw;
|
||||
}
|
||||
if get_file_size(file_path).await >= TEXT_VIEW_SIZE_LIMIT {
|
||||
return ViewType::Raw;
|
||||
}
|
||||
if let Ok(accept) = Accept::parse(req) {
|
||||
for accept_mime in accept.mime_precedence() {
|
||||
if mime_matches(&accept_mime, file_mime) {
|
||||
return ViewType::Raw;
|
||||
}
|
||||
if accept_mime == TEXT_HTML {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ViewType::Raw;
|
||||
}
|
||||
|
||||
ViewType::Html
|
||||
}
|
||||
|
||||
fn mime_matches(accept: &Mime, content: &Mime) -> bool {
|
||||
let type_matches = accept.type_() == content.type_() || accept.type_() == mime::STAR;
|
||||
let subtype_matches = accept.subtype() == content.subtype() || accept.subtype() == mime::STAR;
|
||||
type_matches && subtype_matches
|
||||
}
|
||||
|
||||
async fn get_file_size(file_path: &Path) -> u64 {
|
||||
|
@ -114,7 +150,7 @@ fn build_file_response(
|
|||
file_name: &str,
|
||||
path: async_std::path::PathBuf,
|
||||
content_type: Mime,
|
||||
req: HttpRequest,
|
||||
req: &HttpRequest,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let content_disposition = ContentDisposition {
|
||||
disposition: if download {
|
||||
|
@ -131,7 +167,7 @@ fn build_file_response(
|
|||
})?
|
||||
.set_content_type(content_type)
|
||||
.set_content_disposition(content_disposition);
|
||||
file.into_response(&req)
|
||||
file.into_response(req)
|
||||
}
|
||||
|
||||
fn get_disposition_params(filename: &str) -> Vec<DispositionParam> {
|
||||
|
|
Loading…
Reference in New Issue