Files.walkFileTree()配合SimpleFileVisitor统计文件夹总大小的核心是重写visitFile()累加文件大小,通过AtomicLong线程安全计数,visitFileFailed()控制异常跳过,比Files.walk()更稳健可控。

用 Files.walkFileTree() 配合 SimpleFileVisitor 统计文件夹总大小,核心在于重写 visitFile() 方法累加每个文件的大小,并在遍历完成后返回结果。它比手动递归更安全(自动处理符号链接、权限异常等),也比 Files.walk() 流式 API 更易控制异常行为。
关键步骤:继承 SimpleFileVisitor 并重写 visitFile
你需要一个自定义访问器,继承 SimpleFileVisitor<Path>,并在其中维护一个线程安全的累计变量(如 AtomicLong):
-
只在
visitFile()中累加:该方法对每个普通文件调用一次,可通过Files.size(path)获取字节数 -
谨慎处理异常:重写
visitFileFailed()决定是否跳过无法读取的文件(如权限不足),避免整个遍历中断 -
无需重写
preVisitDirectory():除非你要跳过某些目录(比如.git),否则默认进入所有可访问子目录
完整示例代码
以下是一个简洁可靠的实现:
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicLong;
public class FolderSizeCalculator {
public static long calculateSize(Path root) throws IOException {
AtomicLong size = new AtomicLong(0);
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
try {
size.addAndGet(Files.size(file));
} catch (IOException e) {
// 忽略单个文件读取失败(如被占用、权限问题)
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
// 例如:没有权限访问某个子目录,继续其他路径
return FileVisitResult.CONTINUE;
}
});
return size.get();
}
// 使用示例
public static void main(String[] args) throws IOException {
Path dir = Paths.get("/path/to/your/folder");
long bytes = calculateSize(dir);
System.out.printf("总大小: %d 字节 (%.2f MB)%n", bytes, bytes / 1024.0 / 1024.0);
}
}
注意事项和优化点
-
符号链接默认不跟随:如果想统计链接目标而非链接文件本身,创建
FileTreeOption时传入FileVisitOption.FOLLOW_LINKS -
大文件或海量小文件性能:
Files.size()对每个文件都是系统调用,不可避免;若只需粗略估算,可跳过特殊文件(如设备文件、socket)——但 Java 中需靠attrs.isRegularFile()过滤 -
避免重复统计硬链接:Java NIO 不直接暴露 inode,无法自动去重;如需精确去重,得自行缓存
Files.getAttribute(file, "unix:ino")(仅限 Unix 系统) - 内存安全:整个过程不加载文件内容,只读元数据,内存占用恒定,适合超大目录
对比 Files.walk() 流式方式
虽然 Files.walk() 写起来更短:
long size = Files.walk(path)
.filter(Files::isRegularFile)
.mapToLong(p -> {
try { return Files.size(p); }
catch (IOException e) { return 0; }
})
.sum();
但它在遇到权限异常时会抛出 IOException 中断整个流,且难以精细控制跳过逻辑(比如只想忽略特定错误类型)。而 walkFileTree 的回调机制更稳健、更可控。

















