Can force file download
This commit is contained in:
parent
40cf0c16df
commit
cfbc003737
@ -7,7 +7,7 @@ use crate::models::files::File;
|
||||
use crate::services::files_service;
|
||||
use crate::utils::time_utils;
|
||||
use actix_web::http::header;
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use actix_web::{HttpRequest, HttpResponse, web};
|
||||
use std::ops::Add;
|
||||
use std::time::Duration;
|
||||
|
||||
@ -44,40 +44,60 @@ pub async fn get_info(file_extractor: FileIdExtractor) -> HttpResult {
|
||||
Ok(HttpResponse::Ok().json(file_extractor.as_ref()))
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct DownloadQuery {
|
||||
#[serde(default)]
|
||||
download: bool,
|
||||
}
|
||||
|
||||
/// Download an uploaded file
|
||||
pub async fn download(req: HttpRequest, file_extractor: FileIdExtractor) -> HttpResult {
|
||||
serve_file(req, file_extractor.as_ref()).await
|
||||
pub async fn download(
|
||||
req: HttpRequest,
|
||||
file_extractor: FileIdExtractor,
|
||||
query: web::Query<DownloadQuery>,
|
||||
) -> HttpResult {
|
||||
serve_file(req, file_extractor.as_ref(), query.download).await
|
||||
}
|
||||
|
||||
/// Serve a file, returning 304 status code if the requested file already exists
|
||||
pub async fn serve_file(req: HttpRequest, file: &File) -> HttpResult {
|
||||
// Check if the browser already knows the etag
|
||||
if let Some(c) = req.headers().get(header::IF_NONE_MATCH) {
|
||||
if c.to_str().unwrap_or("") == file.sha512.as_str() {
|
||||
return Ok(HttpResponse::NotModified().finish());
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the browser already knows the file by date
|
||||
if let Some(c) = req.headers().get(header::IF_MODIFIED_SINCE) {
|
||||
let date_str = c.to_str().unwrap_or("");
|
||||
if let Ok(date) = httpdate::parse_http_date(date_str) {
|
||||
if date.add(Duration::from_secs(1))
|
||||
>= time_utils::unix_to_system_time(file.time_create as u64)
|
||||
{
|
||||
pub async fn serve_file(req: HttpRequest, file: &File, download_file: bool) -> HttpResult {
|
||||
if !download_file {
|
||||
// Check if the browser already knows the etag
|
||||
if let Some(c) = req.headers().get(header::IF_NONE_MATCH) {
|
||||
if c.to_str().unwrap_or("") == file.sha512.as_str() {
|
||||
return Ok(HttpResponse::NotModified().finish());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type(file.mime_type.as_str())
|
||||
// Check if the browser already knows the file by date
|
||||
if let Some(c) = req.headers().get(header::IF_MODIFIED_SINCE) {
|
||||
let date_str = c.to_str().unwrap_or("");
|
||||
if let Ok(date) = httpdate::parse_http_date(date_str) {
|
||||
if date.add(Duration::from_secs(1))
|
||||
>= time_utils::unix_to_system_time(file.time_create as u64)
|
||||
{
|
||||
return Ok(HttpResponse::NotModified().finish());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut res = HttpResponse::Ok();
|
||||
res.content_type(file.mime_type.as_str())
|
||||
.insert_header(("etag", file.sha512.as_str()))
|
||||
.insert_header((
|
||||
"last-modified",
|
||||
time_utils::unix_to_http_date(file.time_create as u64),
|
||||
))
|
||||
.body(files_service::get_file_content(file).await?))
|
||||
));
|
||||
|
||||
// Add filename to response headers if requested
|
||||
if download_file {
|
||||
res.insert_header((
|
||||
"content-disposition",
|
||||
format!("attachment; filename={}", file.file_name),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(res.body(files_service::get_file_content(file).await?))
|
||||
}
|
||||
|
||||
/// Delete an uploaded file
|
||||
|
@ -41,7 +41,10 @@ export class FileApi {
|
||||
/**
|
||||
* Get a file download URL
|
||||
*/
|
||||
static DownloadURL(file: UploadedFile): string {
|
||||
return APIClient.backendURL() + `/file/${file.id}/download`;
|
||||
static DownloadURL(file: UploadedFile, forceDownload = false): string {
|
||||
return (
|
||||
APIClient.backendURL() +
|
||||
`/file/${file.id}/download${forceDownload ? "?download=true" : ""}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ export function FileViewerDialog(p: {
|
||||
fileName={p.file.file_name}
|
||||
fileSize={p.file.file_size}
|
||||
url={FileApi.DownloadURL(p.file)}
|
||||
downloadUrl={FileApi.DownloadURL(p.file, true)}
|
||||
mimetype={p.file.mime_type}
|
||||
/>
|
||||
<DialogActions>
|
||||
@ -38,6 +39,7 @@ interface ViewerProps {
|
||||
fileName: string;
|
||||
fileSize: number;
|
||||
url: string;
|
||||
downloadUrl: string;
|
||||
mimetype: string;
|
||||
}
|
||||
|
||||
@ -65,7 +67,7 @@ function DefaultViewer(p: ViewerProps): React.ReactElement {
|
||||
<Typography variant="caption" gutterBottom>
|
||||
{filesize(p.fileSize)}
|
||||
</Typography>
|
||||
<a href={p.url} target="_blank" referrerPolicy="no-referrer">
|
||||
<a href={p.downloadUrl} target="_blank" referrerPolicy="no-referrer">
|
||||
<Button variant="outlined">Download</Button>
|
||||
</a>
|
||||
</Paper>
|
||||
|
Loading…
x
Reference in New Issue
Block a user