Переглянути джерело

当文件进行扫描时,都改为异步的场景

John 1 рік тому
батько
коміт
27fe091138

+ 177 - 183
src-tauri/src/self_plugin/tauri_plugin_file/files.rs

@@ -1,16 +1,13 @@
+use async_std::fs as async_std_fs;
+use async_std::prelude::*;
 use hex;
 use serde::{Deserialize, Serialize};
 use sha2::{Digest as OtherDigest, Sha256};
 use std::ffi::OsStr;
-// 确保导入 `Digest`
-use async_std::fs as async_std_fs;
-use std::fs;
 use std::path::{Path, PathBuf};
-use std::thread;
-use std::time;
+use std::pin::Pin;
 use std::time::UNIX_EPOCH;
 use tauri::command;
-extern crate trash;
 
 #[derive(Debug, Deserialize, Serialize)]
 pub enum FileSizeCategory {
@@ -29,10 +26,9 @@ pub struct FileInfo {
     pub checkbox_all: Option<bool>,
     pub add_type: Option<String>,
     pub pass_type: Option<String>,
-    // pub checked_size_values: Option<Vec<String>>, // 假设值类型为 String,具体类型视情况调整
-    pub checked_size_values: Option<Vec<FileSizeCategory>>, // 使用正确的类型
+    pub checked_size_values: Option<Vec<FileSizeCategory>>,
     pub checkbox_size_all: Option<bool>,
-    pub checked_type_values: Option<Vec<String>>, // 同上
+    pub checked_type_values: Option<Vec<String>>,
     pub time: Option<String>,
     pub id: Option<u32>,
     pub progress: Option<f32>,
@@ -41,7 +37,7 @@ pub struct FileInfo {
 }
 
 #[command]
-pub fn get_all_directory(file_info: FileInfo) -> Vec<FileInfos> {
+pub async fn get_all_directory(file_info: FileInfo) -> Vec<FileInfos> {
     let mut files = Vec::new();
     if let Some(ref path) = file_info.path {
         println!("Processing directory: {}", path);
@@ -52,7 +48,8 @@ pub fn get_all_directory(file_info: FileInfo) -> Vec<FileInfos> {
             &file_info.checked_size_values,
             &file_info.types,
             &file_info.excluded_file_names,
-        );
+        )
+        .await;
         files
     } else {
         files
@@ -74,19 +71,70 @@ pub fn get_file_type_by_path(file_path: String) -> String {
     }
 }
 
-fn read_files_in_directory(
-    dir: &Path,
-    files: &mut Vec<FileInfos>,
-    filters: &Option<Vec<FileSizeCategory>>,
-    types: &Option<Vec<String>>,
-    excluded_file_names: &Option<Vec<String>>,
-) {
-    if dir.is_dir() {
-        if let Ok(entries) = fs::read_dir(dir) {
-            for entry in entries.flatten() {
-                let path = entry.path();
-                if path.is_dir() {
-                    read_files_in_directory(&path, files, filters, types, excluded_file_names);
+fn read_files_in_directory<'a>(
+    dir: &'a Path,
+    files: &'a mut Vec<FileInfos>,
+    filters: &'a Option<Vec<FileSizeCategory>>,
+    types: &'a Option<Vec<String>>,
+    excluded_file_names: &'a Option<Vec<String>>,
+) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
+    Box::pin(async move {
+        let metadata = match async_std_fs::metadata(dir).await {
+            Ok(metadata) => metadata,
+            Err(_) => return,
+        };
+
+        if metadata.is_dir() {
+            let mut entries = match async_std_fs::read_dir(dir).await {
+                Ok(entries) => entries,
+                Err(_) => return,
+            };
+
+            while let Some(entry) = entries.next().await {
+                let entry = match entry {
+                    Ok(entry) => entry,
+                    Err(_) => continue,
+                };
+
+                let path: std::path::PathBuf = entry.path().into();
+
+                if let Some(file_name) = path.file_name().and_then(|name| name.to_str()) {
+                    if file_name.starts_with('.') || file_name.starts_with('$') {
+                        read_files_in_directory(
+                            path.as_path(),
+                            files,
+                            filters,
+                            types,
+                            excluded_file_names,
+                        )
+                        .await;
+                        continue;
+                    }
+                }
+
+                if let Some(excluded_names) = excluded_file_names {
+                    if excluded_names.iter().any(|excluded| {
+                        path.components()
+                            .any(|component| component.as_os_str() == OsStr::new(excluded))
+                    }) {
+                        continue;
+                    }
+                }
+
+                let metadata = match async_std_fs::metadata(&path).await {
+                    Ok(metadata) => metadata,
+                    Err(_) => continue,
+                };
+
+                if metadata.is_dir() {
+                    read_files_in_directory(
+                        path.as_path(),
+                        files,
+                        filters,
+                        types,
+                        excluded_file_names,
+                    )
+                    .await;
                     continue;
                 }
 
@@ -98,24 +146,63 @@ fn read_files_in_directory(
                     }
                 }
 
-                let metadata = if let Ok(meta) = path.metadata() { meta } else { continue };
-                let size_matches = filters.as_ref().map_or(true, |f| file_size_matches(metadata.len(), f));
+                let size_matches = filters
+                    .as_ref()
+                    .map_or(true, |f| file_size_matches(metadata.len(), f));
                 let type_matches = types.as_ref().map_or(true, |t| file_type_matches(&path, t));
                 if size_matches && type_matches {
                     if let Some(path_str) = path.to_str() {
-                        // 确保 path_str 是有效的 UTF-8 字符串
-                        let path_info = get_file_info(path_str.to_string());
-                        // 使用 path_info 做其他事情
+                        let path_info = get_file_info(path_str.to_string()).await;
                         files.push(path_info);
                     } else {
-                        // 处理 path 不是有效 UTF-8 的情况
-                        // eprintln!("Path is not valid UTF-8");
                         continue;
                     }
-
                 }
             }
         }
+    })
+}
+
+#[command]
+pub async fn get_file_info(file_path: String) -> FileInfos {
+    let path = Path::new(&file_path);
+
+    let metadata = match async_std_fs::metadata(&path).await {
+        Ok(meta) => meta,
+        Err(_) => {
+            return FileInfos {
+                file_path: path.to_path_buf(),
+                file_name: None,
+                file_type: None,
+                file_size: 0,
+                modified_time: None,
+                creation_time: None,
+            };
+        }
+    };
+
+    let modified_time = metadata
+        .modified()
+        .ok()
+        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
+        .map(|d| d.as_secs());
+
+    let accessed_time = metadata
+        .accessed()
+        .ok()
+        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
+        .map(|d| d.as_secs());
+
+    FileInfos {
+        file_path: path.to_path_buf(),
+        file_name: path
+            .file_name()
+            .and_then(|name| name.to_str())
+            .map(|name| name.to_string()),
+        file_type: get_file_type(&file_path).map(|t| t.to_string()),
+        file_size: metadata.len(),
+        modified_time,
+        creation_time: accessed_time,
     }
 }
 
@@ -141,32 +228,17 @@ fn file_type_matches(path: &Path, types: &Vec<String>) -> bool {
     false
 }
 
-fn excluded_file_names_matches(path_name: &str, excluded_file_names: &Vec<String>) -> bool {
-    for excluded_name in excluded_file_names {
-        if path_name == excluded_name {
-            return true;
-        }
-    }
-    false
-}
-
 #[command]
-// 定义异步函数来计算文件的 SHA256 哈希
 pub async fn calculate_file_hash(file_path: String) -> String {
-    // 异步读取文件
     let file_bytes = match async_std_fs::read(file_path).await {
         Ok(bytes) => bytes,
-        Err(_) => return "Failed to read file".to_string(), // 如果读取失败,返回错误信息
+        Err(_) => return "Failed to read file".to_string(),
     };
 
-    // 初始化 SHA256 哈希上下文
     let mut hasher = Sha256::new();
     hasher.update(&file_bytes);
 
-    // 完成哈希计算
     let result = hasher.finalize();
-
-    // 将结果转换为十六进制字符串
     hex::encode(result)
 }
 
@@ -176,65 +248,10 @@ pub struct FileInfos {
     file_name: Option<String>,
     file_type: Option<String>,
     file_size: u64,
-    modified_time: Option<u64>, // 时间戳形式
+    modified_time: Option<u64>,
     creation_time: Option<u64>,
 }
 
-#[command]
-pub fn get_file_info(file_path: String) -> FileInfos {
-    let path = Path::new(&file_path);
-
-    // 使用 match 来处理可能的错误
-    let metadata = match fs::metadata(&path) {
-        Ok(meta) => meta,
-        Err(_) => {
-            return FileInfos {
-                // 在这里处理错误,可能是返回默认的 FileInfos
-                file_path: path.to_path_buf(),
-                file_name: None,
-                file_type: None,
-                file_size: 0,
-                modified_time: None,
-                creation_time: None,
-            };
-        }
-    };
-
-    // 获取文件修改时间
-    let modified_time = metadata
-        .modified()
-        .ok()
-        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
-        .map(|d| d.as_secs());
-
-    // 获取文件创建时间
-    let accessed_time = metadata
-        .accessed()
-        .ok()
-        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
-        .map(|d| d.as_secs());
-
-    // 构造 FileInfo 结构
-    FileInfos {
-        file_path: path.to_path_buf(),
-        file_name: path
-            .file_name()
-            .and_then(|name| name.to_str())
-            .map(|name| name.to_string()),
-        file_type: get_file_type(&file_path).map(|t| t.to_string()), // 确保 get_file_type 也不返回 Result 或 Option
-        file_size: metadata.len(),
-        modified_time,
-        creation_time: accessed_time,
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct RequestMvFile {
-    code: Option<u64>,
-    msg: Option<String>,
-    data: Option<String>,
-}
-
 #[command]
 pub fn mv_file_to_trash(file_path: String) -> RequestMvFile {
     let data = file_path.clone();
@@ -254,38 +271,71 @@ pub fn mv_file_to_trash(file_path: String) -> RequestMvFile {
     }
 }
 
+#[derive(Debug, Serialize, Deserialize)]
+pub struct RequestMvFile {
+    code: Option<u64>,
+    msg: Option<String>,
+    data: Option<String>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct MoveFileError {
+    error: String,
+}
+
+impl std::fmt::Display for MoveFileError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.error)
+    }
+}
+
+impl std::error::Error for MoveFileError {}
+
+#[command]
+pub async fn move_specific_files(
+    file_paths: Vec<PathBuf>,
+    dest_dir: &str,
+) -> Result<RequestMvFile, MoveFileError> {
+    let destination = Path::new(dest_dir);
+    if !destination.is_dir() {
+        return Err(MoveFileError {
+            error: "Destination directory does not exist or is not a directory.".to_string(),
+        });
+    }
+
+    for file_path in file_paths {
+        if file_path.is_file() {
+            let dest_file_path =
+                destination.join(file_path.file_name().unwrap_or_else(|| OsStr::new("")));
+            if let Err(e) = async_std_fs::rename(&file_path, &dest_file_path).await {
+                return Err(MoveFileError {
+                    error: format!("Failed to move file '{}': {}", file_path.display(), e),
+                });
+            }
+        } else {
+            return Err(MoveFileError {
+                error: format!("Provided path '{}' is not a file.", file_path.display()),
+            });
+        }
+    }
+
+    Ok(RequestMvFile {
+        code: Some(200),
+        msg: Some("All specified files moved successfully.".to_string()),
+        data: Some("All specified files moved successfully.".to_string()),
+    })
+}
+
 #[command]
 pub fn get_app_data_dir() -> String {
     std::env::var("MY_APP_DATA_DIR")
         .unwrap_or_else(|_| "Environment variable for app data directory not set".to_string())
 }
 
-/* #[command]
-fn open_finder(path: String) -> RequestMvFile {
-    let open_result = std::process::Command::new("open")
-        .arg("-R")
-        // .arg("Finder")
-        .arg(path)
-        .spawn();
-
-    match open_result {
-        Ok(_) => RequestMvFile {
-            code: Some(200),
-            msg: Some("success".to_string()),
-            data: Some("success".to_string()),
-        },
-        Err(e) => RequestMvFile {
-            code: Some(500),
-            msg: Some("error".to_string()),
-            data: Some(e.to_string()),
-        },
-    }
-} */
-
 #[command]
 pub fn show_file_in_explorer(file_path: String) -> RequestMvFile {
     println!("256 {}", file_path);
-    // 获取文件所在的目录
+
     #[cfg(target_os = "linux")]
     let path = std::path::Path::new(&file_path);
     #[cfg(target_os = "linux")]
@@ -332,59 +382,3 @@ pub fn show_file_in_explorer(file_path: String) -> RequestMvFile {
         },
     }
 }
-
-// 批量移动指定的多个文件到一个目标目录
-#[command]
-pub fn move_specific_files(file_paths: Vec<PathBuf>, dest_dir: &str) -> RequestMvFile {
-    // 检查目标目录
-    let destination = Path::new(dest_dir);
-    if !destination.is_dir() {
-        return RequestMvFile {
-            code: Some(400),
-            msg: Some("Destination directory does not exist or is not a directory.".to_string()),
-            data: Some("Destination directory does not exist or is not a directory.".to_string()),
-        };
-    }
-
-    // 遍历提供的文件路径列表
-    for file_path in file_paths {
-        if file_path.is_file() {
-            // 确保路径是文件
-            let dest_file_path =
-                destination.join(file_path.file_name().unwrap_or_else(|| OsStr::new("")));
-            if let Err(e) = fs::rename(&file_path, &dest_file_path) {
-                return RequestMvFile {
-                    code: Some(500),
-                    msg: Some(format!(
-                        "Failed to move file '{}': {}",
-                        file_path.display(),
-                        e
-                    )),
-                    data: Some(format!(
-                        "Failed to move file '{}': {}",
-                        file_path.display(),
-                        e
-                    )),
-                };
-            }
-        } else {
-            return RequestMvFile {
-                code: Some(400),
-                msg: Some(format!(
-                    "Provided path '{}' is not a file.",
-                    file_path.display()
-                )),
-                data: Some(format!(
-                    "Provided path '{}' is not a file.",
-                    file_path.display()
-                )),
-            };
-        }
-    }
-
-    RequestMvFile {
-        code: Some(200),
-        msg: Some("All specified files moved successfully.".to_string()),
-        data: Some("All specified files moved successfully.".to_string()),
-    }
-}

+ 11 - 10
src/pages/DuplicateFile/CalculateDuplicateFiles.tsx

@@ -50,8 +50,8 @@ export default function CalculateDuplicateFiles() {
     // 这段代码只会在组件首次挂载时执行一次
     console.log("组件已挂载");
 
-    console.log(location); // 当前路由路径
-    console.log(location.pathname); // 当前路由路径
+    // console.log(location); // 当前路由路径
+    // console.log(location.pathname); // 当前路由路径
 
     setTimeout(() => {
       // 设置一个状态标志,表示组件已经挂载
@@ -62,7 +62,7 @@ export default function CalculateDuplicateFiles() {
     // 只在组件卸载时设置isCancelled为true
     return () => {
       if (hasMounted) {
-        console.log(47, " 当组件卸载时,设置isCancelled为true");
+        // console.log(47, " 当组件卸载时,设置isCancelled为true");
         setIsCancelled(true);
       }
     };
@@ -112,6 +112,7 @@ export default function CalculateDuplicateFiles() {
       console.log("计算每一个文件的hash 开始");
       try {
         await computeFileChecksums_2();
+        setDuplicateFilesStep('')
       } catch (error) {
         console.log(107, error);
         if (error == "提前终止") {
@@ -140,7 +141,7 @@ export default function CalculateDuplicateFiles() {
         duplicateFiles: "finish",
         done: "finish",
       });
-      navigate("/calculate-list/" + fileId);
+      // navigate("/calculate-list/" + fileId);
     }
   }
 
@@ -161,6 +162,7 @@ export default function CalculateDuplicateFiles() {
       const files = await File.getAllList({
         path: fileInfo.path,
         types,
+        excluded_file_names:['test', 'node_modules']
       });
       setPercent(100);
       console.log("扫描目录文件 结束");
@@ -175,7 +177,8 @@ export default function CalculateDuplicateFiles() {
    * */
   async function computeFileMetadata_v2(files: backFileInfoType[]) {
     const [progressRes] = await get_progress_by_sourceId(`${fileId}`);
-    if (!files.length || !progressRes.total_entries) {
+    // if (!files.length || (!progressRes.total_entries && !progressRes.hash_null_count)) {
+    if (!files.length || progressRes.hash_null_count) {
       setStepsStatus({
         ...stepsStatus,
         scanDir: "finish",
@@ -219,7 +222,6 @@ export default function CalculateDuplicateFiles() {
   // 计算每一个文件的hash
   async function computeFileChecksums_2() {
     const [progressRes] = await get_progress_by_sourceId(`${fileId}`);
-    // console.log(178, progressRes)
 
     // 已经存在的数据中,计算过的 hash 总量跟 文件总数不是一样的,并且存在有记录的文件
     if (progressRes.hash_null_count && progressRes.total_entries) {
@@ -247,14 +249,12 @@ export default function CalculateDuplicateFiles() {
         const [fileinfo, error] = await getFirstEmptyHashBySourceId(
           `${fileId}`,
         );
+        // && fileinfo.file_size / 1024 / 1024 / 1024 < 1 ||
         if (fileinfo) {
           // 获取文件类型和哈希
           const hash = await File.getHash(fileinfo.path);
           await updateFileHsah(fileinfo.path, hash, `${fileId}`);
         }
-        // console.clear();  // 清除控制台
-        // console.log(223, window.location.href, location.pathname, fileinfo);
-        // console.log(223, window.location.href.indexOf(location.pathname), location.pathname);
         fileIndex++;
         // await waittime();
         const [newProgressRes] = await get_progress_by_sourceId(`${fileId}`);
@@ -266,7 +266,7 @@ export default function CalculateDuplicateFiles() {
         );
         return Promise.resolve(0);
       }, Promise.resolve(0));
-
+      setDuplicateFilesStep('');
       setStepsStatus({
         ...stepsStatus,
         scanDir: "finish",
@@ -274,6 +274,7 @@ export default function CalculateDuplicateFiles() {
         duplicateFiles: "finish",
       });
     } else {
+      setDuplicateFilesStep('');
       setStepsStatus({
         ...stepsStatus,
         scanDir: "finish",

+ 2 - 0
src/pages/DuplicateFile/DuplicateFile.tsx

@@ -22,6 +22,7 @@ import type { FixedType } from "rc-table/lib/interface";
 import FileInfoEditer from "./FileInfoEditer";
 import { FileInfoType } from "@/types/files";
 import {
+  closeDB,
   delSelectedFileHistory,
   get_all_history,
   get_info_by_path,
@@ -164,6 +165,7 @@ export default function DuplicateFile() {
   async function delRow(row: FileInfoType) {
     // 删除对应的查询数据库的文件
     const appDataDirPath = await appDataDir();
+    await closeDB(`${row.id}`);
     const dbPath = `${appDataDirPath}/files_${row.id}.db`;
     const dbShmPath = `${appDataDirPath}/files_${row.id}.db-shm`;
     const dbWalPath = `${appDataDirPath}/files_${row.id}.db-wal`;

+ 6 - 0
src/services/file-service.ts

@@ -722,3 +722,9 @@ export async function duplicateFilesDBInit(sourceId: string) {
     console.log(error);
   }
 }
+
+
+export async function closeDB(sourceId: string) {
+  const DB = await Database.load(`sqlite:files_${sourceId}.db`);
+  await DB.close();
+}

+ 1 - 1
src/types/files.d.ts

@@ -68,5 +68,5 @@ export type fileInfoParamsType = {
   path?: string;
   checked_size_values?: string[];
   types?: any[];
-  excluded_file_names?: number;
+  excluded_file_names?: string[];
 };