use chrono::{DateTime, Local};
use std::fs::{self, File, DirEntry};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::vec::IntoIter;
use std::path::{Path, PathBuf};
pub struct FileUtil;
impl FileUtil {
pub fn list(path: &Path, recurse: bool) -> Vec<String> {
let mut result = Vec::new();
let entries = match fs::read_dir(path) {
Ok(entries) => entries,
Err(_) => return result,
};
for entry in entries {
let entry = match entry {
Ok(entry) => entry,
Err(_) => continue,
};
let path = entry.path();
if path.is_dir() {
if recurse {
result.extend(Self::list(&path, true));
}
result.push(path.to_string_lossy().to_string());
} else {
result.push(path.to_string_lossy().to_string());
}
}
result
}
pub fn metadata(file_path: &str) -> io::Result<std::fs::Metadata> {
fs::metadata(file_path)
}
pub fn last_midified(file_path: &str) -> io::Result<String> {
let metadata = fs::metadata(file_path)?;
let last_modified = metadata.modified().unwrap();
let last_modified: DateTime<Local> = last_modified.into();
Ok(last_modified.format("%Y-%m-%d %H:%M:%S").to_string())
}
pub fn read_string(path: &Path) -> Result<String, io::Error> {
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
pub fn read_string_by_iter(file_path: &str) -> Result<impl Iterator, io::Error> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let xx = reader.lines();
Ok(xx)
}
pub fn read_bytes(path: &Path) -> Result<Vec<u8>, io::Error> {
let mut file = File::open(path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
Ok(contents)
}
pub fn write_string(path: &Path, content: String) -> io::Result<()> {
let mut file = File::create(path)?;
file.write_all(content.as_bytes())?;
Ok(())
}
pub fn append_string(path: &Path, content: String) -> io::Result<()> {
let mut file = File::options().append(true).open(path)?;
file.write_all(content.as_bytes())?;
Ok(())
}
pub fn write_bytes(path: &Path, bytes: &[u8]) -> io::Result<()> {
let mut file = File::create(path)?;
file.write_all(bytes)?;
Ok(())
}
pub fn create_dir_with_parents(dir_path: &str) -> io::Result<()> {
fs::create_dir_all(dir_path)
}
pub fn delete_file(file_path: &str) -> io::Result<()> {
fs::remove_file(file_path)
}
pub fn delete_directory(dir_path: &str) -> io::Result<()> {
fs::remove_dir_all(dir_path)
}
pub fn glob(pattern: &str) -> io::Result<Vec<String>> {
let mut results = Vec::new();
let r = glob::glob(pattern);
if r.is_err() {
return Err(io::Error::new(io::ErrorKind::Other, r.err().unwrap().to_string()));
}
for entry in r.unwrap() {
match entry {
Ok(path) => {
let tmp = path.into_os_string().into_string().unwrap();
let tmp = tmp.replace("\\", "/");
results.push(tmp);
},
Err(e) => println!("{:?}", e),
}
}
Ok(results)
}
pub fn recursive_files(path: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let file_iterator = RecursiveFileIterator::new(Path::new(path))?;
let results: Vec<String> = file_iterator.map(|item| {
let t = item.as_os_str().to_os_string().into_string().unwrap();
t.replace("\\", "/")
}).collect();
Ok(results)
}
}
struct RecursiveFileIterator {
current_dir_iter: Option<IntoIter<DirEntry>>,
subdirs: Vec<PathBuf>,
}
impl RecursiveFileIterator {
pub fn new(root: &Path) -> Result<Self, std::io::Error> {
if !root.exists() || !root.is_dir() {
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, "目录不存在或不是目录"))
}
let entries: Vec<DirEntry> = std::fs::read_dir(root)?
.collect::<Result<Vec<DirEntry>, std::io::Error>>()?;
Ok(Self {
current_dir_iter: Some(entries.into_iter()),
subdirs: Vec::new(),
})
}
}
impl Iterator for RecursiveFileIterator {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(iter) = &mut self.current_dir_iter {
if let Some(entry) = iter.next() {
let path = entry.path();
if path.is_dir() {
self.subdirs.push(path);
} else {
return Some(path);
}
} else {
self.current_dir_iter = None;
}
} else {
if let Some(subdir) = self.subdirs.pop() {
match std::fs::read_dir(&subdir) {
Ok(entries) => {
let entries:Vec<DirEntry> = entries.filter_map(|e| e.ok()).collect();
self.current_dir_iter = Some(entries.into_iter());
},
Err(e) => {
eprintln!("无法读取目录:{:?}: {}", subdir, e);
}
}
} else {
return None;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
static ROOT_DIR: &str = "d:/tmp/";
#[test]
fn test_glob() {
let xx = FileUtil::glob("D:/*/sub_dir").unwrap();
println!("{:?}", xx);
}
#[test]
fn test_list() {
let temp_dir = Path::new(ROOT_DIR);
let sub_dir = temp_dir.join("sub_dir");
std::fs::create_dir(&sub_dir).expect("Failed to create sub dir");
let file_path = sub_dir.join("test_file.txt");
std::fs::File::create(&file_path).expect("Failed to create file");
let result = FileUtil::list(temp_dir, true);
assert!(result.contains(&file_path.to_string_lossy().to_string()));
}
#[test]
fn test_read_string() -> io::Result<()> {
let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
println!("{:?}", temp_path);
let mut temp_file = File::options()
.write(true)
.create(true)
.open(temp_path.as_path())?;
let content = "test content".to_string();
temp_file
.write_all(content.as_bytes())
.expect("Failed to write to temp file");
let result = FileUtil::read_string(temp_path.as_path());
assert!(result.is_ok());
assert_eq!(result.unwrap(), content);
Ok(())
}
#[test]
fn test_read_bytes() {
let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
let mut temp_file = File::options()
.write(true)
.open(temp_path.as_path())
.unwrap();
let content = [1, 2, 3];
temp_file
.write_all(&content)
.expect("Failed to write to temp file");
let result = FileUtil::read_bytes(temp_path.as_path());
assert!(result.is_ok());
}
#[test]
fn test_write_string() {
let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
let _temp_file = File::options().write(true).open(&temp_path).unwrap();
let content = "test write content".to_string();
let result = FileUtil::write_string(&temp_path, content.clone());
assert!(result.is_ok());
let read_result = FileUtil::read_string(&temp_path);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), content);
}
#[test]
fn test_append_string() {
let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
let _temp_file = File::open(&temp_path).unwrap();
let initial_content = "initial content".to_string();
let append_content = " appended content".to_string();
FileUtil::write_string(&temp_path, initial_content.clone())
.expect("Failed to write initial content");
let append_result = FileUtil::append_string(&temp_path, append_content.clone());
assert!(append_result.is_ok());
let read_result = FileUtil::read_string(&temp_path);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), initial_content + &append_content);
}
#[test]
fn test_write_bytes() {
let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
let _temp_file = File::open(&temp_path).unwrap();
let content = [4, 5, 6];
let result = FileUtil::write_bytes(&temp_path, &content);
assert!(result.is_ok());
let read_result = FileUtil::read_bytes(&temp_path);
assert!(read_result.is_ok());
assert_eq!(read_result.unwrap(), content);
}
#[test]
fn test_write_bytes_and_read_bytes_with_invalid_path() {
let results = FileUtil::recursive_files(r"D:\tmp\originalTifData\2025-02-24").unwrap();
for r in results {
println!("{}", r);
}
}
}