aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--src/args.rs20
-rw-r--r--src/config.rs23
-rw-r--r--src/renderer.rs5
-rw-r--r--tests/upload_files.rs20
5 files changed, 73 insertions, 2 deletions
diff --git a/README.md b/README.md
index d6bf32f..d613662 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,13 @@ Sometimes this is just a more practical and quick way than doing things properly
(where `$FILE` is the path to the file. This uses miniserve's default port of 8080)
+### Take pictures and upload them from smartphones:
+
+ miniserve -u -m image -q
+
+This uses the `--media-type` option, which sends a hint for the expected media type to the browser.
+Some mobile browsers like Firefox on Android will offer to open the camera app when seeing this.
+
## Features
- Easy to use
diff --git a/src/args.rs b/src/args.rs
index 841db3a..7667bee 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -8,6 +8,13 @@ use crate::auth;
use crate::errors::ContextualError;
use crate::renderer;
+#[derive(clap::ArgEnum, Clone)]
+pub enum MediaType {
+ Image,
+ Audio,
+ Video,
+}
+
#[derive(Parser)]
#[clap(name = "miniserve", author, about, version)]
pub struct CliArgs {
@@ -104,6 +111,19 @@ pub struct CliArgs {
#[clap(short = 'u', long = "upload-files")]
pub file_upload: bool,
+ /// Specify uploadable media types
+ #[clap(arg_enum, short = 'm', long = "media-type", requires = "file-upload")]
+ pub media_type: Option<Vec<MediaType>>,
+
+ /// Directly specify the uploadable media type expression
+ #[clap(
+ short = 'M',
+ long = "raw-media-type",
+ requires = "file-upload",
+ conflicts_with = "media-type"
+ )]
+ pub media_type_raw: Option<String>,
+
/// Enable overriding existing files during file upload
#[clap(short = 'o', long = "overwrite-files")]
pub overwrite_files: bool,
diff --git a/src/config.rs b/src/config.rs
index f140c2a..ef3d1de 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -13,7 +13,10 @@ use http::HeaderMap;
#[cfg(feature = "tls")]
use rustls_pemfile as pemfile;
-use crate::{args::CliArgs, auth::RequiredAuth};
+use crate::{
+ args::{CliArgs, MediaType},
+ auth::RequiredAuth,
+};
/// Possible characters for random routes
const ROUTE_ALPHABET: [char; 16] = [
@@ -81,6 +84,9 @@ pub struct MiniserveConfig {
/// Enable file upload
pub file_upload: bool,
+ /// HTML accept attribute value
+ pub uploadable_media_type: Option<String>,
+
/// Enable upload to override existing files
pub overwrite_files: bool,
@@ -187,6 +193,20 @@ impl MiniserveConfig {
#[cfg(not(feature = "tls"))]
let tls_rustls_server_config = None;
+ let uploadable_media_type = args.media_type_raw.or_else(|| {
+ args.media_type.map(|types| {
+ types
+ .into_iter()
+ .map(|t| match t {
+ MediaType::Audio => "audio/*",
+ MediaType::Image => "image/*",
+ MediaType::Video => "video/*",
+ })
+ .collect::<Vec<_>>()
+ .join(",")
+ })
+ });
+
Ok(MiniserveConfig {
verbose: args.verbose,
path: args.path.unwrap_or_else(|| PathBuf::from(".")),
@@ -206,6 +226,7 @@ impl MiniserveConfig {
overwrite_files: args.overwrite_files,
show_qrcode: args.qrcode,
file_upload: args.file_upload,
+ uploadable_media_type,
tar_enabled: args.enable_tar,
tar_gz_enabled: args.enable_tar_gz,
zip_enabled: args.enable_zip,
diff --git a/src/renderer.rs b/src/renderer.rs
index ef7285b..c64d459 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -107,7 +107,10 @@ pub fn page(
form id="file_submit" action=(upload_action) method="POST" enctype="multipart/form-data" {
p { "Select a file to upload or drag it anywhere into the window" }
div {
- input #file-input type="file" name="file_to_upload" required="" multiple {}
+ @match &conf.uploadable_media_type {
+ Some(accept) => {input #file-input accept=(accept) type="file" name="file_to_upload" required="" multiple {}},
+ None => {input #file-input type="file" name="file_to_upload" required="" multiple {}}
+ }
button type="submit" { "Upload file" }
}
}
diff --git a/tests/upload_files.rs b/tests/upload_files.rs
index 5e764ba..71fcbc4 100644
--- a/tests/upload_files.rs
+++ b/tests/upload_files.rs
@@ -167,3 +167,23 @@ fn upload_to_symlink_directory(
Ok(())
}
+
+/// Test setting the HTML accept attribute using -m and -M.
+#[rstest]
+#[case(server(&["-u"]), None)]
+#[case(server(&["-u", "-m", "image"]), Some("image/*"))]
+#[case(server(&["-u", "-m", "image", "-m", "audio", "-m", "video"]), Some("image/*,audio/*,video/*"))]
+#[case(server(&["-u", "-m", "audio", "-m", "image", "-m", "video"]), Some("audio/*,image/*,video/*"))]
+#[case(server(&["-u", "-M", "test_value"]), Some("test_value"))]
+fn set_media_type(
+ #[case] server: TestServer,
+ #[case] expected_accept_value: Option<&str>,
+) -> Result<(), Error> {
+ let body = reqwest::blocking::get(server.url())?.error_for_status()?;
+ let parsed = Document::from_read(body)?;
+
+ let input = parsed.find(Attr("id", "file-input")).next().unwrap();
+ assert_eq!(input.attr("accept"), expected_accept_value);
+
+ Ok(())
+}