From 577d6246dc81d9151bbcda1cd8b10ffcdd8e6bc8 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Mon, 21 Oct 2024 17:38:05 +0200 Subject: [PATCH] Awnser Range requests and stream files downloads Uses NamedFile from actix_files for unencrypted files instead of reading them into memory and setting them as body. Note the content_type is set by NamedFile now. The code for it is about identical to what was previously there. --- src/endpoints/create.rs | 4 ++++ src/endpoints/file.rs | 43 ++++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/endpoints/create.rs b/src/endpoints/create.rs index d3d6abf..dd4f302 100644 --- a/src/endpoints/create.rs +++ b/src/endpoints/create.rs @@ -70,6 +70,10 @@ pub fn expiration_to_timestamp(expiration: &str, timenow: i64) -> i64 { } } +/// receives a file through http Post on url /upload/a-b-c with a, b and c +/// different animals. The client sends the post in response to a form. +// TODO: form field order might need to be changed. In my testing the attachment +// data is nestled between password encryption key etc <21-10-24, dvdsk> pub async fn create( data: web::Data, mut payload: Multipart, diff --git a/src/endpoints/file.rs b/src/endpoints/file.rs index 289bf66..665c54c 100644 --- a/src/endpoints/file.rs +++ b/src/endpoints/file.rs @@ -1,4 +1,4 @@ -use std::fs::{self, File}; +use std::fs::File; use std::path::PathBuf; use crate::args::ARGS; @@ -7,6 +7,7 @@ use crate::util::misc::remove_expired; use crate::util::{animalnumbers::to_u64, misc::decrypt_file}; use crate::AppState; use actix_multipart::Multipart; +use actix_web::http::header; use actix_web::{get, post, web, Error, HttpResponse}; use futures::TryStreamExt; @@ -57,6 +58,8 @@ pub async fn post_secure_file( pastas[index].id_as_animals() ))?; + // Not compatible with NamedFile from actix_files (it needs a File + // to work therefore secure files do not support streaming let decrypted_data: Vec = decrypt_file(&password, &file)?; // Set the content type based on the file extension @@ -71,6 +74,7 @@ pub async fn post_secure_file( "Content-Disposition", format!("attachment; filename=\"{}\"", pasta_file.name()), )) + // TODO: make streaming <21-10-24, dvdsk> .body(decrypted_data); return Ok(response); } @@ -80,8 +84,9 @@ pub async fn post_secure_file( #[get("/file/{id}")] pub async fn get_file( - data: web::Data, + request: actix_web::HttpRequest, id: web::Path, + data: web::Data, ) -> Result { // get access to the pasta collection let mut pastas = data.pastas.lock().unwrap(); @@ -126,28 +131,18 @@ pub async fn get_file( ); let file_path = PathBuf::from(file_path); - // Read the contents of the file into memory - // let mut file_content = Vec::new(); - // let mut file = File::open(&file_path)?; - // file.read_exact(&mut file_content)?; - - let file_contents = fs::read(&file_path)?; - - // Set the content type based on the file extension - let content_type = mime_guess::from_path(&file_path) - .first_or_octet_stream() - .to_string(); - - // Create an HttpResponse object with the file contents as the response body - let response = HttpResponse::Ok() - .content_type(content_type) - .append_header(( - "Content-Disposition", - format!("attachment; filename=\"{}\"", pasta_file.name()), - )) - .body(file_contents); - - return Ok(response); + // This will stream the file and set the content type based on the + // file path + let file_reponse = actix_files::NamedFile::open(file_path)?; + let file_reponse = file_reponse.set_content_disposition(header::ContentDisposition { + disposition: header::DispositionType::Attachment, + parameters: vec![header::DispositionParam::Filename( + pasta_file.name().to_string(), + )], + }); + // This takes care of streaming/seeking using the Range + // header in the request. + return Ok(file_reponse.into_response(&request)); } }