mirror of
https://github.com/sigoden/dufs.git
synced 2026-04-09 17:13:02 +03:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06d2b81824 | ||
|
|
3673a64ec7 | ||
|
|
d9a917176a | ||
|
|
cdb7b5fc87 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -258,7 +258,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "duf"
|
name = "duf"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-walkdir",
|
"async-walkdir",
|
||||||
"async_zip",
|
"async_zip",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "duf"
|
name = "duf"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["sigoden <sigoden@gmail.com>"]
|
authors = ["sigoden <sigoden@gmail.com>"]
|
||||||
description = "Duf is a simple file server."
|
description = "Duf is a simple file server."
|
||||||
|
|||||||
@@ -36,16 +36,19 @@ You can run this command to start serving your current working directory on 127.
|
|||||||
duf
|
duf
|
||||||
```
|
```
|
||||||
|
|
||||||
...or specify which folder you want to serve:
|
...or specify which folder you want to serve.
|
||||||
|
|
||||||
```
|
```
|
||||||
duf folder_name
|
duf folder_name
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Only serve static files, disable upload and delete operations
|
||||||
|
```
|
||||||
|
duf --static
|
||||||
|
```
|
||||||
|
|
||||||
Finally, run this command to see a list of all available option
|
Finally, run this command to see a list of all available option
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Curl
|
### Curl
|
||||||
|
|
||||||
Download a file
|
Download a file
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ fn app() -> clap::Command<'static> {
|
|||||||
|
|
||||||
let arg_static = Arg::new("static")
|
let arg_static = Arg::new("static")
|
||||||
.long("static")
|
.long("static")
|
||||||
.help("Only serve static files, not allowed to upload or delete file");
|
.help("Only serve static files, disable upload and delete operations");
|
||||||
|
|
||||||
let arg_auth = Arg::new("auth")
|
let arg_auth = Arg::new("auth")
|
||||||
.short('a')
|
.short('a')
|
||||||
|
|||||||
@@ -45,20 +45,21 @@
|
|||||||
class Uploader {
|
class Uploader {
|
||||||
idx = 0;
|
idx = 0;
|
||||||
file;
|
file;
|
||||||
path;
|
|
||||||
$elem;
|
$elem;
|
||||||
constructor(idx, file) {
|
constructor(idx, file) {
|
||||||
this.idx = idx;
|
this.idx = idx;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.path = location.pathname + "/" + file.name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
upload() {
|
upload() {
|
||||||
const { file, idx, path } = this;
|
const { file, idx } = this;
|
||||||
|
let url = location.href.split('?')[0];
|
||||||
|
if (!url.endsWith("/")) url += "/";
|
||||||
|
url += encodeURI(file.name);
|
||||||
$uploaders.insertAdjacentHTML("beforeend", `
|
$uploaders.insertAdjacentHTML("beforeend", `
|
||||||
<div class="uploader path">
|
<div class="uploader path">
|
||||||
<div><svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M6 5H2V4h4v1zM2 8h7V7H2v1zm0 2h7V9H2v1zm0 2h7v-1H2v1zm10-7.5V14c0 .55-.45 1-1 1H1c-.55 0-1-.45-1-1V2c0-.55.45-1 1-1h7.5L12 4.5zM11 5L8 2H1v12h10V5z"></path></svg></div>
|
<div><svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M6 5H2V4h4v1zM2 8h7V7H2v1zm0 2h7V9H2v1zm0 2h7v-1H2v1zm10-7.5V14c0 .55-.45 1-1 1H1c-.55 0-1-.45-1-1V2c0-.55.45-1 1-1h7.5L12 4.5zM11 5L8 2H1v12h10V5z"></path></svg></div>
|
||||||
<a href="${path}" id="file${idx}">${file.name} (0%)</a>
|
<a href="${url}" id="file${idx}">${file.name} (0%)</a>
|
||||||
</div>`);
|
</div>`);
|
||||||
this.$elem = document.getElementById(`file${idx}`);
|
this.$elem = document.getElementById(`file${idx}`);
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
ajax.addEventListener("load", e => this.complete(e), false);
|
ajax.addEventListener("load", e => this.complete(e), false);
|
||||||
ajax.addEventListener("error", e => this.fail(e), false);
|
ajax.addEventListener("error", e => this.fail(e), false);
|
||||||
ajax.addEventListener("abort", e => this.fail(e), false);
|
ajax.addEventListener("abort", e => this.fail(e), false);
|
||||||
ajax.open("PUT", path);
|
ajax.open("PUT", url);
|
||||||
ajax.send(file);
|
ajax.send(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
src/main.rs
18
src/main.rs
@@ -25,14 +25,18 @@ async fn main() {
|
|||||||
async fn run() -> BoxResult<()> {
|
async fn run() -> BoxResult<()> {
|
||||||
let args = Args::parse(matches())?;
|
let args = Args::parse(matches())?;
|
||||||
|
|
||||||
let level = if args.log {
|
if std::env::var("RUST_LOG").is_ok() {
|
||||||
LevelFilter::Info
|
simple_logger::init()?;
|
||||||
} else {
|
} else {
|
||||||
LevelFilter::Error
|
let level = if args.log {
|
||||||
};
|
LevelFilter::Info
|
||||||
simple_logger::SimpleLogger::default()
|
} else {
|
||||||
.with_level(level)
|
LevelFilter::Error
|
||||||
.init()?;
|
};
|
||||||
|
simple_logger::SimpleLogger::default()
|
||||||
|
.with_level(level)
|
||||||
|
.init()?;
|
||||||
|
}
|
||||||
serve(args).await
|
serve(args).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use async_zip::write::{EntryOptions, ZipFileWriter};
|
|||||||
use async_zip::Compression;
|
use async_zip::Compression;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
use hyper::body::Bytes;
|
||||||
use hyper::header::HeaderValue;
|
use hyper::header::HeaderValue;
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
use hyper::{Body, Method, StatusCode};
|
use hyper::{Body, Method, StatusCode};
|
||||||
@@ -15,10 +16,9 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncWrite;
|
use tokio::io::{AsyncReadExt, AsyncWrite};
|
||||||
use tokio::{fs, io};
|
use tokio::{fs, io};
|
||||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||||
use tokio_util::io::ReaderStream;
|
|
||||||
use tokio_util::io::StreamReader;
|
use tokio_util::io::StreamReader;
|
||||||
|
|
||||||
type Request = hyper::Request<Body>;
|
type Request = hyper::Request<Body>;
|
||||||
@@ -35,6 +35,7 @@ macro_rules! status_code {
|
|||||||
|
|
||||||
const INDEX_HTML: &str = include_str!("index.html");
|
const INDEX_HTML: &str = include_str!("index.html");
|
||||||
const INDEX_CSS: &str = include_str!("index.css");
|
const INDEX_CSS: &str = include_str!("index.css");
|
||||||
|
const BUF_SIZE: usize = 1024 * 16;
|
||||||
|
|
||||||
pub async fn serve(args: Args) -> BoxResult<()> {
|
pub async fn serve(args: Args) -> BoxResult<()> {
|
||||||
let address = args.address()?;
|
let address = args.address()?;
|
||||||
@@ -187,10 +188,27 @@ impl InnerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_send_dir_zip(&self, path: &Path) -> BoxResult<Response> {
|
async fn handle_send_dir_zip(&self, path: &Path) -> BoxResult<Response> {
|
||||||
let (mut writer, reader) = tokio::io::duplex(65536);
|
let (mut tx, body) = Body::channel();
|
||||||
dir_zip(&mut writer, path).await?;
|
let (mut writer, mut reader) = tokio::io::duplex(BUF_SIZE);
|
||||||
let stream = ReaderStream::new(reader);
|
let path = path.to_owned();
|
||||||
let body = Body::wrap_stream(stream);
|
tokio::spawn(async move {
|
||||||
|
if let Err(e) = dir_zip(&mut writer, &path).await {
|
||||||
|
error!("Fail to zip {}, {}", path.display(), e.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tokio::spawn(async move {
|
||||||
|
// Reuse this buffer
|
||||||
|
let mut buf = [0_u8; BUF_SIZE];
|
||||||
|
loop {
|
||||||
|
let n = reader.read(&mut buf).await.unwrap();
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tx.send_data(Bytes::from(buf[..n].to_vec())).await).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Ok(Response::new(body))
|
Ok(Response::new(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user