Files.move()不支持跨分区原子移动,需先用Files.getFileStore()判断是否同文件系统:同区则直接move+REPLACE_EXISTING;跨区则必须复制+校验+删除三步,并记录操作ID以便恢复。

Files.move() 本身不支持跨磁盘分区的原子移动,强行调用会直接抛出 IOException(如 Linux/macOS 的 “Invalid cross-device link”,Windows 的 “The process cannot access the file because it is being used by another process” 或 I/O error)。所谓“实战跨分区移动”,本质是**放弃原子性幻想,转为可控、可校验、可回滚的复制+删除流程**。关键不在怎么“用 Files.move”,而在于何时不用它、怎么补足它缺失的能力。
先判断是否真跨分区
别猜,要实测。跨分区的判定必须基于文件系统对象,而非路径字符串:
- 用
Files.getFileStore(path)获取源和目标父目录的FileStore实例,再用.equals()比较——这是最准确的方式 - 轻量替代:调用
path.toRealPath().getRoot(),比较两个 root 是否相同(注意toRealPath()会检查路径存在性并解析符号链接) - 避免用
Path.getParent()或字符串截取盘符(如path.toString().substring(0,2)),在 Linux/NFS/容器等环境下完全失效
同分区:放心用 Files.move + REPLACE_EXISTING
确认同文件系统后,Files.move 是毫秒级、原子、安全的。但仍有细节必须处理:
- 目标路径已存在时,默认抛
FileAlreadyExistsException,必须显式传StandardCopyOption.REPLACE_EXISTING - 若目标是目录,且非空,move 会失败;确保目标不存在,或目标是空目录(Windows 下限制更严)
- 重命名场景(同目录改名)也属于此分支,别漏掉
REPLACE_EXISTING
跨分区:手动 copy + 校验 + delete,三步不可省
这是唯一可靠路径。每一步都可能中断,必须分层防御:
-
复制阶段:用
Files.copy(source, tempTarget, REPLACE_EXISTING),临时路径建议放在目标卷根下(如target.resolveSibling(target.getFileName() + ".tmp")) -
校验阶段:至少比对
Files.size(source)和Files.size(tempTarget);高可靠性场景加哈希(如 SHA-256),用MessageDigest流式计算,避免全量加载内存 -
删除阶段:校验通过后才执行
Files.delete(source);若 delete 失败,保留源文件并报错——此时数据仍在,可人工介入
异常中断后的状态与恢复策略
跨分区操作没有“中间态”概念,只有三种明确状态:
- 复制前中断:源完好,无副作用,重试即可
- 复制完成但未校验/未删除中断:源文件仍存在,临时文件残留;下次运行应先清理同名临时文件,再继续
- 校验通过但 delete 失败中断:源文件还在,目标文件已就位(完整);可标记为“已完成”,跳过或记录警告
建议在操作前生成唯一操作 ID,将源、目标、临时路径、时间戳写入轻量日志(如 JSON 文件),崩溃重启后可依据该日志决定下一步动作,而不是盲目重试。

















