在exFAT硬盘里移动文件,为什么有时仍然必须复制一遍
目录
最近我在测试Find Duplicate Files的文件清理流程时,遇到了一个看起来不太合理的问题。
明明源文件和目标目录都在同一块exFAT外置硬盘里,软件执行“移动”时,为什么还要重新复制文件?
我们平时在访达里移动同一块磁盘上的文件,通常很快。文件再大,也可能一下就完成。所以第一感觉会认为,软件只要修改一下路径就可以了,没有必要重新读写整个文件。
后来继续处理清理流程,我发现这里混在一起的是三件不同的事:普通文件移动、跨卷移动,以及带有防误删要求的安全移动。
它们在界面上都叫“移动”,底层做的事情并不一样。
先说结论
在同一个APFS卷内,文件通常可以通过重命名直接改变路径,不需要复制文件内容。
如果源目录和目标目录不在同一个卷,移动本质上只能是:
复制文件 → 确认目标完整 → 删除源文件同一个exFAT卷内虽然也能进行普通重命名,但Find Duplicate Files需要的是一种“目标文件不能已经存在”的原子移动。
当前使用的严格移动方式在exFAT上可能不被支持。如果改用普通重命名,软件就失去了原子防覆盖保证。
因此在不能证明安全重命名成立时,我宁可让它复制一遍,也不让它直接冒险移动。
这并不代表所有exFAT文件移动都必须复制,而是清理工具采用了更严格的安全边界。
访达里的“移动”只是一个界面概念
用户看到的移动,是文件从一个目录出现在另一个目录。
文件系统可以用不同方式完成它。
如果源文件和目标目录在同一个卷,最快的方法通常只是修改目录记录。文件内容还在原来的存储位置,没有重新读写几GB甚至几十GB的数据。
如果跨越两个卷,例如从Mac内置硬盘移动到外置硬盘,就不能只修改目录记录。两个卷有各自独立的文件系统,必须先把数据写到目标卷,再删除源卷上的文件。
所以“都在同一块硬盘里”也不一定代表可以直接移动。一块硬盘可能被划分成多个卷,从一个卷移动到另一个卷,仍然属于跨卷操作。
APFS为什么可以直接移动
在同一个APFS卷里,macOS可以使用带有排他语义的原子重命名。
Find Duplicate Files使用这类能力时,会要求目标路径原本不存在。只要同名文件已经存在,操作就应该失败,而不是静默覆盖。
这个条件对清理工具非常重要。
例如软件准备把:
/资料/重复文件.zip移动到:
/隔离区/重复文件.zip如果隔离区里已经存在同名文件,软件不能猜测哪一个应该保留,也不能先删除目标文件再继续。它应该停止操作,把冲突显示给用户处理。
APFS上的原子排他重命名可以把“确认目标不存在”和“完成移动”作为一次文件系统操作。成功就是路径已经切换,失败时源文件仍然留在原处。
这种情况下没有必要复制文件内容,速度也会很快。
exFAT的问题不是不能重命名
exFAT当然支持普通的文件和目录移动。
真正的问题是,它不一定支持清理工具需要的那套原子排他语义。严格移动调用可能直接返回不支持,普通重命名虽然有机会成功,却不能提供完全相同的防覆盖保证。
如果为了速度直接退回普通重命名,就会产生新的问题:
- 目标路径在检查后、移动前被其他程序创建。
- 同名文件被覆盖或替换。
- 软件崩溃后无法准确判断源文件和目标文件的状态。
- 批量清理时,界面预览的范围和真正执行的范围不再一致。
这些情况不一定经常发生,但清理工具处理的是用户原始文件。不能因为概率低,就把后果交给用户承担。
所以我现在的策略是:能证明同卷原子移动安全时才直接移动;不能证明时,进入复制和校验流程。
安全复制不是复制完就删除
这里的复制也不是简单执行一次复制命令,然后马上删除源文件。
一个比较完整的过程应该是:
- 冻结这次确认过的文件路径和文件身份。
- 在目标位置排他创建暂存文件,禁止覆盖现有文件。
- 分块复制文件内容。
- 把事务状态写入恢复记录。
- 完整读回目标文件并重新计算SHA-256。
- 确认目标大小、内容和准备移动的源文件一致。
- 更新事务检查点。
- 最后才删除源文件。
任何一步失败,都不应该继续删除源文件。
例如复制完成后目标文件无法完整读回,说明“写入完成”并不能证明文件真的可以正常读取。即使路径、大小和修改时间看起来都对,也必须保留源文件并把这次操作标记为需要检查。
这也是为什么目标文件要完整读回一次。稳定的文件身份只能说明“还是这个文件”,不能证明写进去的每个字节都正确。
一次失败反而证明了安全策略有效
我在一次外置硬盘测试里遇到过暂存失败。
最终报告是30个文件失败,成功清理0个文件,释放空间0字节。
从结果看,这次操作完全没有完成,体验当然不好。但检查以后发现,失败发生在文件被排他暂存之前,没有进入复制和删除阶段,所有源文件仍然存在。
这正是我希望看到的失败方式。
文件工具不应该为了让报告看起来成功,就在安全条件不足时继续向后执行。失败可以重新处理,源文件被错误删除就不一定能恢复。
为什么这种移动会比较慢
安全复制至少会产生几次完整I/O:
- 从源文件读取数据。
- 把数据写入目标文件。
- 再次完整读取目标文件进行校验。
文件很大、机械硬盘速度较低,或者同一块外置硬盘同时承担读取和写入时,等待时间会比较明显。
这部分可以优化,但不能通过取消最终校验来优化。
我更愿意做的是减少重复的无效读取、使用顺序分块读写,并限制同一块exFAT设备上的并发任务。这样可以减少随机寻道和I/O争用,同时继续保留目标文件完整读回、源文件重新校验和事务恢复记录。
性能优化应该删除重复工作,不能删除安全证据。
能不能让用户自己选择普通移动
技术上可以增加一个“更快但降低保护”的选项,在同卷exFAT上尝试普通重命名。
但这个选项不能悄悄成为默认值。
如果以后提供,我认为至少要明确告诉用户:
- 这种模式不具备相同的原子防覆盖保证。
- 目标目录出现同名文件时必须停止。
- 批量操作前仍然需要生成清单和事务记录。
- 重要资料应先备份。
对于普通文件管理器,用户主动拖动一个文件,可以立即看到冲突并选择怎么处理。重复文件清理工具一次可能处理几百、几千个文件,更应该采用保守默认值。
我的结论
“移动文件”不一定只是修改路径。
同卷APFS、同卷exFAT和跨卷移动,底层能力和安全保证都不同。对普通使用来说,成功到达目标目录可能就够了;对清理工具来说,还要证明没有覆盖其他文件、目标内容完整、事务可以恢复,并且只有在这些条件全部成立后才能删除源文件。
所以在exFAT硬盘里看到软件重新复制文件,不一定是程序写得低效。它可能是在用更多时间换取一个很朴素的保证:如果移动失败,原文件还在。
我做Find Duplicate Files时会继续保留这条边界。可以慢一点,也可以让一次操作失败,但不能为了让进度条更快,就省掉删除源文件之前最关键的证明。
关于这个工具整体的防误删思路,可以继续看重复文件清理工具最危险的不是漏扫,而是误删。
系列:技术
该系列自动来自分类: 技术