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::services::files_service;
|
||||||
use crate::utils::time_utils;
|
use crate::utils::time_utils;
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use actix_web::{HttpRequest, HttpResponse};
|
use actix_web::{HttpRequest, HttpResponse, web};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -44,13 +44,24 @@ pub async fn get_info(file_extractor: FileIdExtractor) -> HttpResult {
|
|||||||
Ok(HttpResponse::Ok().json(file_extractor.as_ref()))
|
Ok(HttpResponse::Ok().json(file_extractor.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct DownloadQuery {
|
||||||
|
#[serde(default)]
|
||||||
|
download: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Download an uploaded file
|
/// Download an uploaded file
|
||||||
pub async fn download(req: HttpRequest, file_extractor: FileIdExtractor) -> HttpResult {
|
pub async fn download(
|
||||||
serve_file(req, file_extractor.as_ref()).await
|
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
|
/// Serve a file, returning 304 status code if the requested file already exists
|
||||||
pub async fn serve_file(req: HttpRequest, file: &File) -> HttpResult {
|
pub async fn serve_file(req: HttpRequest, file: &File, download_file: bool) -> HttpResult {
|
||||||
|
if !download_file {
|
||||||
// Check if the browser already knows the etag
|
// Check if the browser already knows the etag
|
||||||
if let Some(c) = req.headers().get(header::IF_NONE_MATCH) {
|
if let Some(c) = req.headers().get(header::IF_NONE_MATCH) {
|
||||||
if c.to_str().unwrap_or("") == file.sha512.as_str() {
|
if c.to_str().unwrap_or("") == file.sha512.as_str() {
|
||||||
@ -69,15 +80,24 @@ pub async fn serve_file(req: HttpRequest, file: &File) -> HttpResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(HttpResponse::Ok()
|
let mut res = HttpResponse::Ok();
|
||||||
.content_type(file.mime_type.as_str())
|
res.content_type(file.mime_type.as_str())
|
||||||
.insert_header(("etag", file.sha512.as_str()))
|
.insert_header(("etag", file.sha512.as_str()))
|
||||||
.insert_header((
|
.insert_header((
|
||||||
"last-modified",
|
"last-modified",
|
||||||
time_utils::unix_to_http_date(file.time_create as u64),
|
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
|
/// Delete an uploaded file
|
||||||
|
@ -41,7 +41,10 @@ export class FileApi {
|
|||||||
/**
|
/**
|
||||||
* Get a file download URL
|
* Get a file download URL
|
||||||
*/
|
*/
|
||||||
static DownloadURL(file: UploadedFile): string {
|
static DownloadURL(file: UploadedFile, forceDownload = false): string {
|
||||||
return APIClient.backendURL() + `/file/${file.id}/download`;
|
return (
|
||||||
|
APIClient.backendURL() +
|
||||||
|
`/file/${file.id}/download${forceDownload ? "?download=true" : ""}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ export function FileViewerDialog(p: {
|
|||||||
fileName={p.file.file_name}
|
fileName={p.file.file_name}
|
||||||
fileSize={p.file.file_size}
|
fileSize={p.file.file_size}
|
||||||
url={FileApi.DownloadURL(p.file)}
|
url={FileApi.DownloadURL(p.file)}
|
||||||
|
downloadUrl={FileApi.DownloadURL(p.file, true)}
|
||||||
mimetype={p.file.mime_type}
|
mimetype={p.file.mime_type}
|
||||||
/>
|
/>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
@ -38,6 +39,7 @@ interface ViewerProps {
|
|||||||
fileName: string;
|
fileName: string;
|
||||||
fileSize: number;
|
fileSize: number;
|
||||||
url: string;
|
url: string;
|
||||||
|
downloadUrl: string;
|
||||||
mimetype: string;
|
mimetype: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +67,7 @@ function DefaultViewer(p: ViewerProps): React.ReactElement {
|
|||||||
<Typography variant="caption" gutterBottom>
|
<Typography variant="caption" gutterBottom>
|
||||||
{filesize(p.fileSize)}
|
{filesize(p.fileSize)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<a href={p.url} target="_blank" referrerPolicy="no-referrer">
|
<a href={p.downloadUrl} target="_blank" referrerPolicy="no-referrer">
|
||||||
<Button variant="outlined">Download</Button>
|
<Button variant="outlined">Download</Button>
|
||||||
</a>
|
</a>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user