From ae11a39804a3db280a087906ce42084baef22a3e Mon Sep 17 00:00:00 2001 From: sigoden Date: Sat, 28 May 2022 18:58:43 +0800 Subject: [PATCH] feat: download folder as zip file --- Cargo.lock | 390 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 4 +- README.md | 9 +- src/args.rs | 11 +- src/index.css | 6 + src/index.html | 44 +++++- src/server.rs | 45 +++++- 7 files changed, 483 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a980b8..f290aa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,105 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-compression" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +dependencies = [ + "bzip2", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "xz2", + "zstd", + "zstd-safe", +] + +[[package]] +name = "async-fs" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-lock" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-task" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" + +[[package]] +name = "async-walkdir" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826d88d73e87e7504b635b6e427561faa6a65f4a2f59e75efcbfa51a0876bb90" +dependencies = [ + "async-fs", + "futures-lite", +] + +[[package]] +name = "async_io_utilities" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0248112abfeab682c97306bc1e180ee957260107a55a437cedf9a3acca92135e" +dependencies = [ + "tokio", +] + +[[package]] +name = "async_zip" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a5c419dca9559f15d04befbf9ff01c39ca16d4c0abd56f60daaf87a386b929" +dependencies = [ + "async-compression", + "async_io_utilities", + "chrono", + "crc32fast", + "thiserror", + "tokio", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + [[package]] name = "atty" version = "0.2.14" @@ -31,18 +130,81 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + [[package]] name = "bytes" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + [[package]] name = "clap" version = "3.1.18" @@ -77,9 +239,29 @@ dependencies = [ ] [[package]] -name = "duf" -version = "0.1.0" +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ + "cache-padded", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "duf" +version = "0.2.0" +dependencies = [ + "async-walkdir", + "async_zip", "base64", "clap", "futures", @@ -93,6 +275,33 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "event-listener" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "flate2" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -147,6 +356,21 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.21" @@ -239,9 +463,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ "bytes", "futures-channel", @@ -262,20 +486,38 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" dependencies = [ "autocfg", "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "itoa" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -297,12 +539,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lzma-sys" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.3" @@ -311,10 +573,29 @@ checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -346,6 +627,12 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "percent-encoding" version = "2.1.0" @@ -364,6 +651,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + [[package]] name = "proc-macro2" version = "1.0.39" @@ -428,7 +721,7 @@ dependencies = [ "atty", "colored", "log", - "time", + "time 0.3.9", "winapi", ] @@ -465,6 +758,37 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time" version = "0.3.9" @@ -576,6 +900,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "want" version = "0.3.0" @@ -586,6 +916,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -656,3 +992,41 @@ name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "xz2" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.1+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index dd785e2..5739f50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "duf" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["sigoden "] description = "Duf is a simple file server." @@ -23,6 +23,8 @@ futures = "0.3" base64 = "0.13" log = "0.4" simple_logger = "2.1.0" +async_zip = "0.0.7" +async-walkdir = "0.2.0" [profile.release] lto = true diff --git a/README.md b/README.md index 38f00f4..9d5018c 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,12 @@ Duf is a simple file server. -![demo](https://user-images.githubusercontent.com/4012553/170498429-d68a5d6e-c4c5-405e-9c95-2ec880e2678d.png) +![demo](https://user-images.githubusercontent.com/4012553/170822562-c6594de5-0bb2-4d5e-ba66-5731ab6481fd.png) ## Features - Serve static files +- Download folder as zip file - Upload files - Delete files - Basic authentication @@ -51,9 +52,13 @@ Download a file ``` curl http://127.0.0.1:5000/some-file -curl -o some-file.zip http://127.0.0.1:5000/some-file.zip +curl -o some-file2 http://127.0.0.1:5000/some-file ``` +Download a folder as zip file + +curl -o some-folder.zip http://127.0.0.1:5000/some-folder?zip + Upload a file ``` diff --git a/src/args.rs b/src/args.rs index 8771c26..1b324a6 100644 --- a/src/args.rs +++ b/src/args.rs @@ -29,10 +29,9 @@ fn app() -> clap::Command<'static> { .allow_invalid_utf8(true) .help("Path to a directory for serving files"); - let arg_readonly = Arg::new("readonly") - .short('r') - .long("readonly") - .help("Only serve static files, no operations like upload and delete"); + let arg_static = Arg::new("static") + .long("static") + .help("Only serve static files, not allowed to upload or delete file"); let arg_auth = Arg::new("auth") .short('a') @@ -49,7 +48,7 @@ fn app() -> clap::Command<'static> { .arg(arg_address) .arg(arg_port) .arg(arg_path) - .arg(arg_readonly) + .arg(arg_static) .arg(arg_auth) .arg(arg_no_log) } @@ -78,7 +77,7 @@ impl Args { let port = matches.value_of_t::("port")?; let path = matches.value_of_os("path").unwrap_or_default(); let path = Args::parse_path(path)?; - let readonly = matches.is_present("readonly"); + let readonly = matches.is_present("static"); let auth = matches.value_of("auth").map(|v| v.to_owned()); let log = !matches.is_present("no-log"); diff --git a/src/index.css b/src/index.css index fcb7821..c0d56c1 100644 --- a/src/index.css +++ b/src/index.css @@ -46,6 +46,7 @@ html { .upload-control { cursor: pointer; + padding-left: 0.25em; } .main { @@ -80,6 +81,7 @@ html { .main .cell-actions { width: 100px; + display: flex; padding-left: 0.6em; } @@ -108,6 +110,10 @@ html { text-decoration: underline; } +.action-btn { + padding-left: 0.4em; +} + .uploaders { display: flex; flex-wrap: wrap; diff --git a/src/index.html b/src/index.html index 552beef..592b811 100644 --- a/src/index.html +++ b/src/index.html @@ -10,6 +10,11 @@
+
+ + + +
@@ -20,6 +25,7 @@ Name Date modify Size + Actions @@ -100,22 +106,45 @@ } function addPath(file, index) { - const actionTd = readonly ? "" : ` + const url = encodeURI(file.path); + let actionDelete = ""; + let actionDownload = ""; + if (file.path_type.endsWith("Dir")) { + actionDownload = ` +
+ + + +
`; + } else { + actionDownload = ` +
+ + + +
`; + } + if (!readonly) { + actionDelete = ` +
+ +
`; + } + const actionCell = ` -
- -
+ ${actionDownload} + ${actionDelete} ` $tbody.insertAdjacentHTML("beforeend", `
${getSvg(file.path_type)}
- ${file.name} + ${file.name} ${formatMtime(file.mtime)} ${formatSize(file.size)} - ${actionTd} + ${actionCell} `) } @@ -137,7 +166,7 @@ $head.insertAdjacentHTML("beforeend", `
@@ -187,7 +216,6 @@ paths.forEach((file, index) => addPath(file, index)); if (!readonly) { addUploadControl(); - document.querySelector(".main thead tr").insertAdjacentHTML("beforeend", `Actions`); document.getElementById("file").addEventListener("change", e => { const files = e.target.files; for (const file of files) { diff --git a/src/server.rs b/src/server.rs index 41b6d76..f4967b4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,9 @@ use crate::{Args, BoxResult}; +use async_walkdir::WalkDir; +use async_zip::write::{EntryOptions, ZipFileWriter}; +use async_zip::Compression; +use futures::stream::StreamExt; use futures::TryStreamExt; use hyper::header::HeaderValue; use hyper::service::{make_service_fn, service_fn}; @@ -10,8 +14,11 @@ use std::convert::Infallible; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::SystemTime; +use tokio::fs::File; +use tokio::io::AsyncWrite; use tokio::{fs, io}; use tokio_util::codec::{BytesCodec, FramedRead}; +use tokio_util::io::ReaderStream; use tokio_util::io::StreamReader; type Request = hyper::Request; @@ -100,7 +107,11 @@ impl InnerService { match fs::metadata(&path).await { Ok(meta) => { if meta.is_dir() { - self.handle_send_dir(path.as_path()).await + if req.uri().query().map(|v| v == "zip").unwrap_or_default() { + self.handle_send_dir_zip(path.as_path()).await + } else { + self.handle_send_dir(path.as_path()).await + } } else { self.handle_send_file(path.as_path()).await } @@ -175,6 +186,14 @@ impl InnerService { Ok(hyper::Response::builder().body(output.into()).unwrap()) } + async fn handle_send_dir_zip(&self, path: &Path) -> BoxResult { + let (mut writer, reader) = tokio::io::duplex(65536); + dir_zip(&mut writer, path).await?; + let stream = ReaderStream::new(reader); + let body = Body::wrap_stream(stream); + Ok(Response::new(body)) + } + async fn handle_send_file(&self, path: &Path) -> BoxResult { let file = fs::File::open(path).await?; let stream = FramedRead::new(file, BytesCodec::new()); @@ -294,3 +313,27 @@ fn normalize_path>(path: P) -> String { path.to_string() } } + +async fn dir_zip(writer: &mut W, dir: &Path) -> BoxResult<()> { + let mut writer = ZipFileWriter::new(writer); + let mut walkdir = WalkDir::new(dir); + while let Some(entry) = walkdir.next().await { + if let Ok(entry) = entry { + let meta = fs::symlink_metadata(entry.path()).await?; + if meta.is_file() { + let filepath = entry.path(); + let filename = match filepath.strip_prefix(dir).ok().and_then(|v| v.to_str()) { + Some(v) => v, + None => continue, + }; + let entry_options = EntryOptions::new(filename.to_owned(), Compression::Deflate); + let mut file = File::open(&filepath).await?; + let mut file_writer = writer.write_entry_stream(entry_options).await?; + io::copy(&mut file, &mut file_writer).await?; + file_writer.close().await?; + } + } + } + writer.close().await?; + Ok(()) +}