directory downloads (zipped)
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
use std::{os::unix::fs::MetadataExt, path::PathBuf};
|
||||
use std::{
|
||||
io::{Cursor, Write},
|
||||
os::unix::fs::MetadataExt,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use futures_util::TryStreamExt;
|
||||
use rustix::fs::{Statx, statx};
|
||||
use tokio::{
|
||||
fs,
|
||||
io::{self, AsyncWriteExt as _},
|
||||
fs::{self},
|
||||
io::{self, AsyncReadExt as _, AsyncWriteExt as _},
|
||||
};
|
||||
use tokio_util::io::ReaderStream;
|
||||
|
||||
@@ -214,7 +218,48 @@ impl FileSystem {
|
||||
.open(&path)
|
||||
.await?;
|
||||
|
||||
let file_size = file.metadata().await?.size();
|
||||
let metadata = file.metadata().await?;
|
||||
|
||||
if metadata.is_dir() {
|
||||
drop(file);
|
||||
|
||||
let options = zip::write::SimpleFileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Zstd)
|
||||
.unix_permissions(0o755);
|
||||
|
||||
let mut file_buf = Vec::new();
|
||||
let zip_buf = Vec::new();
|
||||
let cursor = Cursor::new(zip_buf);
|
||||
let mut zip = zip::ZipWriter::new(cursor);
|
||||
|
||||
for entry_path_buf in walk_dir(&path).await? {
|
||||
let entry_path = entry_path_buf.as_path();
|
||||
let entry_str = entry_path
|
||||
.strip_prefix(&path)?
|
||||
.to_str()
|
||||
.context("Failed to get directory entry name")?;
|
||||
|
||||
if entry_path.is_dir() {
|
||||
zip.add_directory(entry_str, options)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
zip.start_file(entry_str, options)?;
|
||||
let mut entry_file = tokio::fs::File::open(entry_path).await?;
|
||||
entry_file.read_to_end(&mut file_buf).await?;
|
||||
zip.write_all(&file_buf)?;
|
||||
file_buf.clear();
|
||||
}
|
||||
|
||||
let mut cursor = zip.finish()?;
|
||||
|
||||
cursor.set_position(0);
|
||||
let stream = FileStream::new(ReaderStream::new(cursor));
|
||||
|
||||
return Ok(stream);
|
||||
}
|
||||
|
||||
let file_size = metadata.size();
|
||||
|
||||
if file_size > self.max_file_fetch_bytes {
|
||||
bail!("File size exceeds configured limit");
|
||||
@@ -410,3 +455,27 @@ where
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async fn walk_dir<P>(dir: P) -> Result<Vec<PathBuf>, tokio::io::Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut dirs = vec![dir.as_ref().to_owned()];
|
||||
let mut files = vec![];
|
||||
|
||||
while !dirs.is_empty() {
|
||||
let mut dir_iter = tokio::fs::read_dir(dirs.remove(0)).await?;
|
||||
|
||||
while let Some(entry) = dir_iter.next_entry().await? {
|
||||
let entry_path_buf = entry.path();
|
||||
|
||||
if entry_path_buf.is_dir() {
|
||||
dirs.push(entry_path_buf);
|
||||
} else {
|
||||
files.push(entry_path_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user