You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Rust(Tauri)中压缩解压时保留文件修改时间的跨平台方案

解决方案:跨平台保留游戏存档的修改时间(Tauri + Rust + zip)

一、压缩阶段:方案可靠性确认与文件夹递归处理

1. 单文件压缩的可靠性

你当前通过SimpleFileOptions::last_modified_time存储原始修改时间的方案是可靠的,zip规范原生支持存储文件最后修改时间,zip crate的实现完全符合标准。可以优化代码的内存效率:

  • 无需将整个文件读入Vec<u8>,改用流式写入避免大文件占用过多内存:
let mut original_file = File::open(&unit_path)?;
let metadata = original_file.metadata()?;
let last_modify_time = chrono::DateTime::<Local>::from(metadata.modified()?)
    .naive_local()
    .try_into()
    .unwrap();

zip.start_file(
    unit_path.file_name()
        .ok_or(BackupFileError::NonePathError)?
        .to_str()
        .ok_or(BackupFileError::NonePathError)?,
    SimpleFileOptions::default()
        .compression_method(zip::CompressionMethod::Bzip2)
        .last_modified_time(last_modify_time),
)?;
std::io::copy(&mut original_file, &mut zip)?; // 流式写入替代read_to_end

2. 文件夹递归压缩的正确姿势

处理文件夹时,需要递归遍历所有子文件并在zip中保留原目录结构:

use walkdir::WalkDir; // 添加walkdir依赖简化遍历

fn add_dir_to_zip(zip: &mut zip::ZipWriter<File>, dir_path: &Path) -> Result<(), BackupFileError> {
    for entry in WalkDir::new(dir_path) {
        let entry = entry?;
        let path = entry.path();
        let relative_path = path.strip_prefix(dir_path)?;

        if path.is_dir() {
            // 在zip中创建对应目录
            zip.add_directory(
                relative_path.to_str().ok_or(BackupFileError::NonePathError)?,
                Default::default()
            )?;
        } else {
            let mut file = File::open(path)?;
            let metadata = file.metadata()?;
            let last_modify_time = chrono::DateTime::<Local>::from(metadata.modified()?)
                .naive_local()
                .try_into()
                .unwrap();

            zip.start_file(
                relative_path.to_str().ok_or(BackupFileError::NonePathError)?,
                SimpleFileOptions::default()
                    .compression_method(zip::CompressionMethod::Bzip2)
                    .last_modified_time(last_modify_time),
            )?;
            std::io::copy(&mut file, zip)?;
        }
    }
    Ok(())
}

二、解压阶段:解决Windows权限问题与跨平台时间设置

1. 权限错误的根源与修复

Windows下Permission denied的核心原因:

  • File::open(&original_path)默认是只读模式,无权限修改文件元数据;
  • 若文件被其他进程占用(如游戏未关闭),也会触发权限错误。

修改后的解压代码:

use filetime::FileTime; // 添加filetime依赖,跨平台处理文件时间

let option = fs_extra::file::CopyOptions::new().overwrite(true);
// 获取zip条目时间信息
let zip_entry = zip.by_name(
    original_path.file_name().unwrap().to_str().unwrap()
).unwrap();
let last_modified = zip_entry.last_modified().unwrap();

// 转换为跨平台兼容的FileTime
let file_time = FileTime::from_last_modification_time(&last_modified);

// 先移动文件到最终路径
fs_extra::file::move_file(&original_path, &unit_path, &option)?;

// 以可写模式打开文件并设置修改时间
let mut file = std::fs::OpenOptions::new()
    .write(true)
    .open(&unit_path)?;
filetime::set_file_handle_mtime(&mut file, file_time)?;

关键优化:

  • 先完成文件移动,再修改目标路径下的文件元数据,避免临时文件权限问题;
  • 用filetime替代手动时间转换,它原生支持Windows、Linux、macOS的文件时间设置;
  • 显式开启write权限确保能修改元数据。

2. 文件夹解压的递归处理

对应压缩逻辑,解压时需先创建目录,再为每个文件恢复修改时间:

fn extract_zip_with_metadata(zip_path: &Path, dest_dir: &Path) -> Result<(), BackupFileError> {
    let file = File::open(zip_path)?;
    let mut zip = zip::ZipArchive::new(file)?;

    for i in 0..zip.len() {
        let mut entry = zip.by_index(i)?;
        let entry_path = dest_dir.join(entry.name());

        if entry.is_dir() {
            std::fs::create_dir_all(&entry_path)?;
        } else {
            // 创建父目录
            if let Some(parent) = entry_path.parent() {
                std::fs::create_dir_all(parent)?;
            }
            // 提取文件
            let mut file = File::create(&entry_path)?;
            std::io::copy(&mut entry, &mut file)?;

            // 恢复修改时间
            let last_modified = entry.last_modified().unwrap();
            let file_time = FileTime::from_last_modification_time(&last_modified);
            filetime::set_file_mtime(&entry_path, file_time)?;
        }
    }
    Ok(())
}

三、跨平台额外注意事项

  • 权限继承:Linux和macOS下,zip会存储文件权限位,解压时zip crate会自动恢复,无需额外处理;
  • Tauri配置:确保tauri.conf.json中配置了必要的文件系统权限:
{
  "tauri": {
    "allowlist": {
      "fs": {
        "readFile": true,
        "writeFile": true,
        "createDir": true,
        "copyFile": true,
        "scope": ["$APPDATA/*", "$HOME/*"] // 按需限定路径范围
      }
    }
  }
}

内容的提问来源于stack exchange,提问作者Sworld

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 16:30:11