1. 概述

在本教程中,我们将学习如何使用 Java 来获取文件夹的大小。内容涵盖从 Java 6、Java 7 到 Java 8 的实现方式,同时也包括使用 Guava 和 Apache Commons IO 的方法。

最后,我们还会展示如何将目录大小转换为人类可读的格式(如 KB、MB 等)。

2. 使用 Java 原生方式

我们先从一个基础的递归方法开始,通过累加目录中所有文件的大小来计算整个目录的大小:

private long getFolderSize(File folder) {
    long length = 0;
    File[] files = folder.listFiles();

    int count = files.length;

    for (int i = 0; i < count; i++) {
        if (files[i].isFile()) {
            length += files[i].length();
        }
        else {
            length += getFolderSize(files[i]);
        }
    }
    return length;
}

可以这样测试 getFolderSize() 方法:

@Test
public void whenGetFolderSizeRecursive_thenCorrect() {
    long expectedSize = 12607;

    File folder = new File("src/test/resources");
    long size = getFolderSize(folder);

    assertEquals(expectedSize, size);
}

⚠️ 注意:listFiles() 方法用于列出目录中的所有内容。

3. 使用 Java 7 NIO.2

在 Java 7 中,我们可以使用 Files.walkFileTree() 方法遍历文件树,从而更优雅地处理目录大小的计算:

@Test
public void whenGetFolderSizeUsingJava7_thenCorrect() throws IOException {
    long expectedSize = 12607;

    AtomicLong size = new AtomicLong(0);
    Path folder = Paths.get("src/test/resources");

    Files.walkFileTree(folder, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
          throws IOException {
            size.addAndGet(attrs.size());
            return FileVisitResult.CONTINUE;
        }
    });

    assertEquals(expectedSize, size.longValue());
}

✅ 这里我们使用了文件树遍历 + 访问者模式,代码结构清晰,易于扩展。

4. 使用 Java 8 Stream API

Java 8 引入了 Stream API,可以用更函数式的方式实现目录大小计算:

@Test
public void whenGetFolderSizeUsingJava8_thenCorrect() throws IOException {
    long expectedSize = 12607;

    Path folder = Paths.get("src/test/resources");
    long size = Files.walk(folder)
      .filter(p -> p.toFile().isFile())
      .mapToLong(p -> p.toFile().length())
      .sum();

    assertEquals(expectedSize, size);
}

✅ 使用 Files.walk() 遍历路径,结合 filtermapToLong,最后通过 sum() 得到总大小,代码简洁高效。

5. 使用 Apache Commons IO

如果你的项目中已经引入了 Apache Commons IO,那么获取目录大小就非常简单了:

@Test
public void whenGetFolderSizeUsingApacheCommonsIO_thenCorrect() {
    long expectedSize = 12607;

    File folder = new File("src/test/resources");
    long size = FileUtils.sizeOfDirectory(folder);

    assertEquals(expectedSize, size);
}

⚠️ 注意:这个方法内部其实也是用 Java 6 的递归实现的。

此外,还有 FileUtils.sizeOfDirectoryAsBigInteger() 方法,可以更好地处理权限受限的目录。

6. 使用 Guava

Guava 也提供了便捷的方法来处理目录遍历:

@Test 
public void whenGetFolderSizeUsingGuava_thenCorrect() { 
    long expectedSize = 12607; 
    File folder = new File("src/test/resources"); 
   
    Iterable<File> files = Files.fileTraverser().breadthFirst(folder);
    long size = StreamSupport.stream(files.spliterator(), false) 
      .filter(f -> f.isFile()) 
      .mapToLong(File::length)
      .sum(); 
   
    assertEquals(expectedSize, size); 
}

✅ Guava 的 fileTraverser() 方法提供了广度优先的目录遍历能力,配合 Java 8 的 Stream 使用效果很好。

7. 转换为人类可读格式

获取字节大小后,我们通常希望将其转换为更直观的单位(如 KB、MB):

@Test
public void whenGetReadableSize_thenCorrect() {
    File folder = new File("src/test/resources");
    long size = getFolderSize(folder);

    String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
    int unitIndex = (int) (Math.log10(size) / 3);
    double unitValue = 1 << (unitIndex * 10);

    String readableSize = new DecimalFormat("#,##0.#")
                                .format(size / unitValue) + " " 
                                + units[unitIndex];
    assertEquals("12.3 KB", readableSize);
}

✅ 使用 DecimalFormat 来控制小数位数,使输出更美观。

8. 注意事项

在处理目录大小时需要注意以下几点:

  • 如果安全管理器拒绝访问,Files.walk()Files.walkFileTree() 会抛出 SecurityException
  • 如果目录中包含符号链接,可能会导致无限循环 ❌

9. 总结

本教程展示了多种获取目录大小的方法,包括:

  • Java 原生递归实现
  • Java 7 的 NIO.2 API
  • Java 8 的 Stream 方式
  • Apache Commons IO
  • Guava

所有示例代码均可在 GitHub 项目 中找到,这是一个 Maven 项目,可直接导入运行。


原始标题:Java - Directory Size

« 上一篇: Baeldung周报46
» 下一篇: Baeldung周报47