pub use crate::core::error2::Error;
pub use crate::core::error2::Result;
use actix_multipart::Field;
use futures_util::TryStreamExt as _;
use md5::{Digest, Md5};
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;
use futures_util::stream::StreamExt;
use tokio_util::io::StreamReader;
pub fn file_stem(file_name: &str) -> String {
std::path::Path::new(&file_name)
.file_stem()
.unwrap()
.to_string_lossy()
.into_owned()
}
pub fn file_name(file_name: &str) -> String {
std::path::Path::new(&file_name)
.file_name()
.unwrap()
.to_string_lossy()
.into_owned()
}
pub fn file_ext(file_name: &str) -> String {
std::path::Path::new(&file_name)
.extension()
.unwrap()
.to_string_lossy()
.into_owned()
}
pub async fn save_upload(
mut upload_file: actix_multipart::Field,
) -> Result<(String, std::path::PathBuf, String)> {
let curr_file_name: String;
if let Some(name) = upload_file.content_disposition().unwrap().get_filename() {
curr_file_name = name.to_string();
} else {
return Err(Error::run_time("Cannot get the upload filename"));
}
let file_name = format!("{}-{}", uuid::Uuid::now_v7(), curr_file_name);
let destination = std::path::PathBuf::from(super::upload_folder()).join(file_name.clone());
let mut saved_file = std::fs::File::create(&destination).map_err(|e| anyhow::anyhow!(e))?;
while let Ok(Some(chunk)) = upload_file.try_next().await {
saved_file
.write_all(&chunk)
.map_err(|e| anyhow::anyhow!(e))?;
}
let file_md5 = super::file_md5(&destination);
Ok((curr_file_name, destination, file_md5))
}
pub fn field_to_futures_reader(field: Field) -> impl tokio::io::AsyncRead {
let stream = field.map(|res| res.map_err(|e| std::io::Error::other(format!("{}", e))));
StreamReader::new(stream)
}
pub fn extract_filename(field: &Field) -> Result<String> {
let cd = field
.content_disposition()
.ok_or_else(|| anyhow::anyhow!("Missing content disposition"))?;
let filename = cd
.get_filename()
.ok_or_else(|| anyhow::anyhow!("Missing filename in content disposition"))?;
Ok(filename.to_string())
}
pub fn path_name(path: &std::path::Path) -> String {
path.file_name().unwrap().to_string_lossy().into_owned()
}
pub fn file_md5(file_path: &std::path::Path) -> String {
let mut file = File::open(file_path).unwrap();
let mut context = Md5::default();
let mut buffer = [0; 1024];
loop {
let n = file.read(&mut buffer).unwrap();
if n == 0 {
break;
}
context.update(&buffer[..n]);
}
let result = format!("{:x}", context.finalize());
result
}
pub fn list_files(dir: std::path::PathBuf, _ext: &str) -> Result<Vec<std::path::PathBuf>> {
let paths = std::fs::read_dir(dir)?
.filter_map(|res| res.ok())
.map(|dir_entry| dir_entry.path())
.filter_map(|path| {
if path.extension().is_some_and(|ext| ext == _ext) {
Some(path)
} else {
None
}
})
.collect::<Vec<_>>();
Ok(paths)
}
pub fn list_all_files(dir: &str, _ext: &str) -> anyhow::Result<Vec<PathBuf>> {
let mut files = Vec::new();
for entry in glob::glob(dir)? {
let path = entry?;
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some(_ext) {
files.push(path);
}
}
Ok(files)
}