give zipped files a containing directory to unzip into
This commit is contained in:
parent
faf018a0a5
commit
e05886aac5
6 changed files with 126 additions and 69 deletions
|
@ -6,6 +6,7 @@ use actix_web::web;
|
|||
use actix_web_actors::ws::{self, CloseCode};
|
||||
use bytes::Bytes;
|
||||
use log::{debug, error, info, trace};
|
||||
use sanitise_file_name::{sanitise_with_options, Options as SanOptions};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
@ -13,6 +14,7 @@ use unicode_normalization::UnicodeNormalization;
|
|||
use crate::{
|
||||
log_auth_failure,
|
||||
store::{self, FileAddError, StoredFile},
|
||||
zip::FileSet,
|
||||
AppState,
|
||||
};
|
||||
|
||||
|
@ -20,8 +22,18 @@ const MAX_FILES: usize = 256;
|
|||
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
|
||||
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
|
||||
|
||||
fn sanitise(name: &str) -> String {
|
||||
sanitise_file_name::sanitise(&name.nfd().collect::<String>())
|
||||
/// Sanitises a filename after performing unicode normalization,
|
||||
/// optionally reducing the length limit to leave space for an
|
||||
/// extension yet to be added.
|
||||
fn sanitise(name: &str, extension_length: usize) -> String {
|
||||
let name = name.nfd().collect::<String>();
|
||||
sanitise_with_options(
|
||||
&name,
|
||||
&SanOptions {
|
||||
length_limit: SanOptions::DEFAULT.length_limit - extension_length,
|
||||
..SanOptions::DEFAULT
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -108,7 +120,7 @@ pub struct UploadedFile {
|
|||
impl UploadedFile {
|
||||
fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self {
|
||||
Self {
|
||||
name: sanitise(name),
|
||||
name: sanitise(name, 0),
|
||||
size,
|
||||
modtime,
|
||||
}
|
||||
|
@ -256,7 +268,7 @@ impl Uploader {
|
|||
let mut file = raw_file.process();
|
||||
while filenames.contains(&file.name) {
|
||||
info!("Duplicate file name: {}", file.name);
|
||||
if file.name.len() >= sanitise_file_name::Options::DEFAULT.length_limit {
|
||||
if file.name.len() >= SanOptions::DEFAULT.length_limit {
|
||||
return Err(Error::DuplicateFilename);
|
||||
}
|
||||
file.name.insert(0, '_');
|
||||
|
@ -278,25 +290,28 @@ impl Uploader {
|
|||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&storage_path)?;
|
||||
let (writer, name, size, modtime): (Box<dyn Write>, _, _, _) = if files.len() > 1 {
|
||||
let (writer, name, size, modtime, contents): (Box<dyn Write>, _, _, _, _) = if files.len() > 1 {
|
||||
info!("Wrapping in zipfile generator");
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let zip_writer = super::zip::ZipGenerator::new(files.clone(), writer);
|
||||
let size = zip_writer.total_size();
|
||||
let download_filename = collection_name
|
||||
.map(|f| sanitise(&(f + ".zip")))
|
||||
.unwrap_or_else(|| {
|
||||
super::APP_NAME.to_owned()
|
||||
+ &now.format(FILENAME_DATE_FORMAT).unwrap()
|
||||
+ ".zip"
|
||||
let collection_name =
|
||||
collection_name.map(|f| sanitise(&f, 4)).unwrap_or_else(|| {
|
||||
super::APP_NAME.to_owned() + &now.format(FILENAME_DATE_FORMAT).unwrap()
|
||||
});
|
||||
(Box::new(zip_writer), download_filename, size, now)
|
||||
let file_set = FileSet {
|
||||
files,
|
||||
directory_name: Some(collection_name.clone()),
|
||||
};
|
||||
let zip_writer =
|
||||
super::zip::ZipGenerator::new(file_set.clone(), writer);
|
||||
let size = zip_writer.total_size();
|
||||
(Box::new(zip_writer), collection_name + ".zip", size, now, Some(file_set))
|
||||
} else {
|
||||
(
|
||||
Box::new(writer),
|
||||
files[0].name.clone(),
|
||||
files[0].size,
|
||||
files[0].modtime,
|
||||
None
|
||||
)
|
||||
};
|
||||
self.writer = Some(writer);
|
||||
|
@ -305,7 +320,7 @@ impl Uploader {
|
|||
size,
|
||||
modtime,
|
||||
expiry: OffsetDateTime::now_utc() + lifetime * time::Duration::DAY,
|
||||
contents: if files.len() > 1 { Some(files) } else { None },
|
||||
contents,
|
||||
};
|
||||
let state = self.app_state.clone();
|
||||
let storage_filename = self.storage_filename.clone();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue