Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added HTML and Javascript progress bar when uploading files #1431

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ Options:

[env: MINISERVE_ALLOWED_UPLOAD_DIR=]

--web-upload-files-concurrency <WEB_UPLOAD_CONCURRENCY>
Configure amount of concurrent uploads when visiting the website. Must have upload-files option enabled for this setting to matter.

[env: MINISERVE_WEB_UPLOAD_CONCURRENCY=]
[default: 0]

-U, --mkdir
Enable creating directories

Expand Down
156 changes: 156 additions & 0 deletions data/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,143 @@ body {
padding: 1.5rem 5rem;
}

$upload_container_height: 18rem;

.upload_area {
display: block;
position: fixed;
bottom: 1rem;
right: -105%;
color: #ffffff;
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3);
background: linear-gradient(135deg, #73a5ff, #5477f5);
padding: 0px;
margin: 0px;
opacity: 1; // Change this
transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
border-radius: 4px;
text-decoration: none;
min-width: 400px;
max-width: 600px;
z-index: 2147483647;
max-height: $upload_container_height;
overflow: hidden;

&.active {
right: 1rem;
}
}

.upload_container {
max-height: $upload_container_height;
display: flex;
flex-direction: column;
}

.upload_header {
padding: 1rem;
background-color: var(--upload_modal_header_background);
color: var(--upload_modal_header_color);
}

.upload_action {
background-color: var(--upload_modal_sub_header_background);
color: var(--upload_modal_header_color);
padding: 0.25rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.75em;
font-weight: 500;
}

.upload_cancel {
background: none;
border: none;
font-weight: 500;
cursor: pointer;
}

.upload_files {
padding: 0px;
margin: 0px;
flex: 1;
overflow-y: auto;
max-height:inherit;
}

.upload_file_list {
background-color: var(--upload_modal_file_item_background);
color: var(--upload_modal_file_item_color);
padding: 0px;
margin: 0px;
list-style: none;
list-style: none;
display: flex;
flex-direction: column;
align-items: stretch;
overflow-y: scroll;
}

.upload_file_container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1rem calc(1rem - 2px) 1rem;
}

.upload_file_action {
display: flex;
justify-content: right;
}

.file_progress_bar {
width: 0%;
border-top: 2px solid var(--progress_bar_background);
transition: width 0.25s ease;

&.cancelled {
border-color: var(--error_color);
}

&.failed {
border-color: var(--error_color);
}

&.complete {
border-color: var(--success_color);
}
}

.upload_file_text {
font-size: 0.80em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

&.cancelled {
text-decoration: line-through;
}

&.failed {
text-decoration: line-through;
}
}

.file_cancel_upload {
padding-left: 0.25rem;
font-size: 1em;
cursor: pointer;
border: none;
background: inherit;
font-size: 1em;
color: var(--error_color);

&.complete {
color: var(--success_color);
}
}

.title {
word-break: break-all;
}
Expand Down Expand Up @@ -524,6 +661,25 @@ th span.active span {
.back {
right: 1.5rem;
}

$upload_container_height_mobile: 60vh;

.upload_area {
width: 100vw;
max-height: $upload_container_height_mobile;
max-width: unset;
min-width: unset;
bottom: 0;

&.active {
right: 0;
left: 0;
}
}

.upload_container {
max-height: $upload_container_height_mobile;
}
}

@media (max-width: 600px) {
Expand Down
8 changes: 8 additions & 0 deletions data/themes/archlinux.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ $generate_default: true !default;
--size_text_color: #fefefe;
--error_color: #e44b4b;
--footer_color: #8eabcc;
--success_color: #52e28a;
--upload_modal_header_background: #5294e2;
--upload_modal_header_color: #eeeeee;
--upload_modal_sub_header_background: #35547a;
--upload_modal_file_item_background: #eeeeee;
--upload_modal_file_item_color: #111111;
--upload_modal_file_upload_complete_background: #cccccc;
--progress_bar_background: #5294e2;
};

@if $generate_default {
Expand Down
8 changes: 8 additions & 0 deletions data/themes/monokai.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ $generate_default: true !default;
--size_text_color: #f8f8f2;
--error_color: #d02929;
--footer_color: #56c9df;
--success_color: #52e28a;
--upload_modal_header_background: #75715e;
--upload_modal_header_color: #eeeeee;
--upload_modal_sub_header_background: #323129;
--upload_modal_file_item_background: #eeeeee;
--upload_modal_file_item_color: #111111;
--upload_modal_file_upload_complete_background: #cccccc;
--progress_bar_background: #5294e2;
};

@if $generate_default {
Expand Down
8 changes: 8 additions & 0 deletions data/themes/squirrel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ $generate_default: true !default;
--size_text_color: #ffffff;
--error_color: #d02424;
--footer_color: #898989;
--success_color: #52e28a;
--upload_modal_header_background: #323232;
--upload_modal_header_color: #eeeeee;
--upload_modal_sub_header_background: #171616;
--upload_modal_file_item_background: #eeeeee;
--upload_modal_file_item_color: #111111;
--upload_modal_file_upload_complete_background: #cccccc;
--progress_bar_background: #5294e2;
};

@if $generate_default {
Expand Down
8 changes: 8 additions & 0 deletions data/themes/zenburn.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ $generate_default: true !default;
--size_text_color: #efefef;
--error_color: #d06565;
--footer_color: #bfaf9f;
--success_color: #52e28a;
--upload_modal_header_background: #7f9f7f;
--upload_modal_header_color: #eeeeee;
--upload_modal_sub_header_background: #404e40;
--upload_modal_file_item_background: #eeeeee;
--upload_modal_file_item_color: #111111;
--upload_modal_file_upload_complete_background: #cccccc;
--progress_bar_background: #5294e2;
};

@if $generate_default {
Expand Down
10 changes: 9 additions & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,17 @@ pub struct CliArgs {
/// The provided path is not a physical file system path. Instead, it's relative to the serve
/// dir. For instance, if the serve dir is '/home/hello', set this to '/upload' to allow
/// uploading to '/home/hello/upload'.
/// When specified via environment variable, a path always neesd to the specified.
/// When specified via environment variable, a path always needs to the specified.
#[arg(short = 'u', long = "upload-files", value_hint = ValueHint::FilePath, num_args(0..=1), value_delimiter(','), env = "MINISERVE_ALLOWED_UPLOAD_DIR")]
pub allowed_upload_dir: Option<Vec<PathBuf>>,

/// Configure amount of concurrent uploads when visiting the website. Must have
/// upload-files option enabled for this setting to matter.
///
/// For example, a value of 4 would mean that the web browser will only upload
/// 4 files at a time to the web server when using the web browser interface.
#[arg(long = "web-upload-files-concurrency", env = "MINISERVE_WEB_UPLOAD_CONCURRENCY", default_value = "0")]
pub web_upload_concurrency: usize,

/// Enable creating directories
#[arg(
Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ pub struct MiniserveConfig {
/// Enable file upload
pub file_upload: bool,

/// Max amount of concurrency when uploading multiple files
pub web_upload_concurrency: usize,

/// List of allowed upload directories
pub allowed_upload_dir: Vec<String>,

Expand Down Expand Up @@ -301,6 +304,7 @@ impl MiniserveConfig {
show_qrcode: args.qrcode,
mkdir_enabled: args.mkdir_enabled,
file_upload: args.allowed_upload_dir.is_some(),
web_upload_concurrency: args.web_upload_concurrency,
allowed_upload_dir,
uploadable_media_type,
tar_enabled: args.enable_tar,
Expand Down
1 change: 1 addition & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ where
let res = fut.await?.map_into_boxed_body();

if (res.status().is_client_error() || res.status().is_server_error())
&& res.request().path() != "/upload"
&& res
.headers()
.get(header::CONTENT_TYPE)
Expand Down
18 changes: 13 additions & 5 deletions src/file_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::ErrorKind;
use std::path::{Component, Path, PathBuf};

use actix_web::{http::header, web, HttpRequest, HttpResponse};
use futures::TryFutureExt;
use futures::{StreamExt, TryFutureExt};
use futures::TryStreamExt;
use serde::Deserialize;
use tokio::fs::File;
Expand All @@ -20,7 +20,7 @@ use crate::{
///
/// Returns total bytes written to file.
async fn save_file(
field: actix_multipart::Field,
field: &mut actix_multipart::Field,
file_path: PathBuf,
overwrite_files: bool,
) -> Result<u64, RuntimeError> {
Expand All @@ -33,8 +33,8 @@ async fn save_file(
RuntimeError::InsufficientPermissionsError(file_path.display().to_string()),
),
Err(err) => Err(RuntimeError::IoError(
format!("Failed to create {}", file_path.display()),
err,
format!("Failed to create {}", file_path.display()),
err,
)),
Ok(v) => Ok(v),
}?;
Expand Down Expand Up @@ -164,7 +164,15 @@ async fn handle_multipart(
}
}

save_file(field, path.join(filename_path), overwrite_files).await
match save_file(&mut field, path.join(filename_path), overwrite_files).await {
Ok(bytes) => Ok(bytes),
Err(err) => {
// Required for file upload. If entire stream is not consumed, javascript
// XML HTTP Request will never complete.
while field.next().await.is_some() {}
Err(err)
},
}
}

/// Query parameters used by upload and rm APIs
Expand Down
Loading