files.copy() 复制大文件卡住或失败主因是原子复制不兼容fat32等文件系统;应禁用原子操作、避免copy_attributes、超2gb改用transferto()流式处理。

Files.copy() 为什么复制大文件会卡住或失败
不是 Files.copy() 本身有问题,而是默认用的“原子复制”在某些文件系统(比如 FAT32、网络挂载盘)上不支持,直接抛 java.nio.file.FileSystemException 或静默卡死。
实操建议:
- 加
StandardCopyOption.REPLACE_EXISTING避免目标已存在导致失败 - 对不确定文件系统的场景,显式禁用原子操作:
StandardCopyOption.COPY_ATTRIBUTES不要乱加,它会触发底层属性拷贝,FAT32 没有权限位就崩 - 真正大文件(>2GB)别依赖单次
Files.copy(),它内部没做流控,内存缓冲可能溢出;该切Files.newInputStream()+ 手动transferTo()
Files.walk() 遍历含符号链接的目录时陷入死循环
默认 Files.walk() 不跳过符号链接,如果目录结构里有软链指向父目录(比如 node_modules/.bin 指向 ../),就会无限递归下去,最终抛 java.nio.file.FileSystemLoopException。
实操建议:
- 必须加
FileVisitOption.FOLLOW_LINKS的反向控制:不传这个选项,链接会被当普通文件跳过;但真要遍历时,改用Files.walkFileTree()配合自定义SimpleFileVisitor - 在
visitFileFailed()里 catchFileSystemLoopException并 returnFileVisitResult.CONTINUE,否则遍历直接中断 - Windows 上 NTFS 符号链接和 junction point 行为不同,
Files.isSymbolicLink()能识别前者,后者得靠Files.getAttribute(path, "dos:reparsePoint")
Files.writeString() 写中文乱码的三个隐藏条件
Files.writeString() 默认用 UTF-8,但乱码往往不是编码问题,而是终端/IDE/文件查看器没匹配——或者你压根没指定 Charset。
实操建议:
- 永远显式传
StandardCharsets.UTF_8,哪怕看着像默认值;JDK 17+ 在某些 locale 下默认是 ISO-8859-1 - 如果写入内容来自用户输入(比如 Scanner.nextLine()),确认输入流本身编码是否一致;
System.in受file.encodingJVM 参数影响,和Files.writeString()的 Charset 可能错位 - Windows 记事本打开 UTF-8 文件时,若无 BOM 会误判为 GBK——这不是 Java 错,但你会以为写错了;加 BOM 要自己拼:
"\uFEFF" + content
Files.createDirectories() 在 NFS 挂载点上返回 false 却不报错
这个方法声明返回 Path,但实际执行中如果父目录权限不足、NFS 服务端配置了 root_squash、或者挂载用了 noexec,它可能静默失败,后续 Files.write() 才爆出 AccessDeniedException。
实操建议:
- 调用后立刻
Files.exists()+Files.isDirectory()双检,别信返回值 - NFS 场景下避免链式调用:
Files.createDirectories(p).resolve("x.txt"),中间路径一旦没建出来,resolve()还是返回逻辑路径,不校验真实存在 - 跨平台部署时,Linux 和 macOS 对 sticky bit、setgid 目录的处理差异会导致
createDirectories()权限继承异常;用PosixFilePermissions.asFileAttribute()显式设权限更稳
复杂点不在 API 多难记,而在于每个方法背后都连着操作系统语义。比如 Files.move() 在 Windows 上是 rename,在 Linux 上可能 fallback 到 copy+delete——这些边界行为,文档里只字不提,但线上一出问题就是数据不一致。










