获取文件夹或文件的大小

121

如何在Java中获取文件夹或文件的大小?


同样的问题,但侧重于效率:https://dev59.com/5nVD5IYBdhLWcg3wBWvd - Ciro Santilli OurBigBook.com
如果你使用的是Android系统,可以看一下StatFs。它使用文件系统统计信息,比递归方法快近1000倍,而我们的需求无法使用递归方法。我们的实现可以在这里找到:https://dev59.com/M6rka4cB1Zd3GeqPc2Sn#58418639 - Joshua Pinter
19个回答

203
java.io.File file = new java.io.File("myfile.txt");
file.length();

如果文件存在,则返回文件的字节数长度,否则返回0。没有内置的方法可以获取文件夹的大小,您需要递归遍历目录树(使用表示目录的文件对象的listFiles()方法)并自己累加目录大小:

public static long folderSize(File directory) {
    long length = 0;
    for (File file : directory.listFiles()) {
        if (file.isFile())
            length += file.length();
        else
            length += folderSize(file);
    }
    return length;
}

警告: 此方法不足以用于生产环境。 directory.listFiles() 可能会返回 null 并引发 NullPointerException 异常。此外,它不考虑符号链接和其他可能的故障模式。请使用此方法


11
如果您在Windows机器的C:根目录下运行此命令,请小心;那里有一个系统文件,根据java.io.File,它既不是文件也不是目录。您可能需要更改else语句以检查该文件是否实际上是一个目录。 - Paul Clapham
2
在方法开始时检查参数是否不是目录,然后返回长度,这样递归就更简单了 - 只需在同一方法中添加对self的调用,这样就支持传入文件引用而不仅仅是目录。 - Kevin Brock
3
如果您使用Java 7或更高版本,请使用答案http://stackoverflow.com/a/19877372/40064,它速度快得多。 - Wim Deblauwe
1
这可能会被符号链接所困扰。此外,如果目录同时被修改,它可能会抛出“NullPointerException”。 - Aleksandr Dubinsky

48
使用Java 7 NIO API,可以更快地计算文件夹大小。
以下是一个可直接运行的示例,它非常健壮,不会抛出异常。它将记录无法进入或遍历困难的目录。符号链接被忽略,并发修改目录不会引起更多不必要的麻烦。
/**
 * Attempts to calculate the size of a file or directory.
 * 
 * <p>
 * Since the operation is non-atomic, the returned value may be inaccurate.
 * However, this method is quick and does its best.
 */
public static long size(Path path) {

    final AtomicLong size = new AtomicLong(0);

    try {
        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {

                size.addAndGet(attrs.size());
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {

                System.out.println("skipped: " + file + " (" + exc + ")");
                // Skip folders that can't be traversed
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) {

                if (exc != null)
                    System.out.println("had trouble traversing: " + dir + " (" + exc + ")");
                // Ignore errors traversing a folder
                return FileVisitResult.CONTINUE;
            }
        });
    } catch (IOException e) {
        throw new AssertionError("walkFileTree will not throw IOException if the FileVisitor does not");
    }

    return size.get();
}

使用AtomicLong而不是仅使用long有什么原因吗? - Lukas Schmelzeisen
1
当从匿名类访问变量时,该变量必须是final的。 - Aksel Willgert
2
我使用JMH进行了基准测试,与commons-io代码相比,这个NIO api方法的速度大约快4到5倍(在一个包含许多子文件夹共计180229个文件的文件夹上进行了测试)。Commons IO花费了15秒,而NIO只需要5秒。 - Wim Deblauwe
5
这种方法最为健壮,它处理符号链接、并发修改、安全异常等情况,可同时处理文件和目录等。很遗憾Files不直接支持它! - Aleksandr Dubinsky
@Alexanderdubinsky,也许是吧,但你破坏了格式。 - Aksel Willgert
显示剩余2条评论

41

您需要使用FileUtils#sizeOfDirectory(File),它来自于commons-io

请注意,您需要手动检查文件是否为目录,因为如果传递非目录,则该方法会抛出异常。

警告:此方法(截至commons-io 2.4)存在错误,如果目录同时被修改,可能会抛出IllegalArgumentException异常。


当文件不是目录时会发生什么?当它不存在时会怎样?等等——文档写得太糟糕了。 - Mr_and_Mrs_D
@Mr_and_Mrs_D - 只需复制并粘贴 checkDirectory(directory); 检查后的代码行。请确保 File.listFiles 有子项。 参考文献: FileUtils.sizeOfDirectory(), File.listFiles() - Mr. Polywhirl
4
查看 bug IO-449。如果正在遍历的目录被修改,此方法将抛出 IllegalArgumentException 异常。 - Aleksandr Dubinsky
哎呀!太糟糕了...是的,它列出文件,如果在代码运行时删除了其中一个文件,它就会抛出异常。 - Dean Hiller

23

在Java 8中:

long size = Files.walk(path).mapToLong( p -> p.toFile().length() ).sum();

在map步骤中使用Files::size会更好,但它会抛出一个受检异常。

更新:
您还应该注意,如果某些文件/文件夹不可访问,这也可能会引发异常。请参见此问题和另一种使用Guava的解决方案。


1
我正在研究类似的东西,最终得到了这个代码:https://dev59.com/k2Ah5IYBdhLWcg3wDfuy,正如你所看到的,还有另一个错误处理方面也会引起问题。 - Aksel Willgert
@AkselWillgert 谢谢,这很不幸,我已经更新了答案。现在切换到 Guava https://dev59.com/k2Ah5IYBdhLWcg3wDfuy#24757556 - Andrejs

10
public static long getFolderSize(File dir) {
    long size = 0;
    for (File file : dir.listFiles()) {
        if (file.isFile()) {
            System.out.println(file.getName() + " " + file.length());
            size += file.length();
        }
        else
            size += getFolderSize(file);
    }
    return size;
}

1
@Vishal,你的代码需要进行简单修复,在递归调用中,你应该将大小添加到现有大小中,而不仅仅是赋值给它。 size += getFolderSize(file); - Teja Kantamneni
@Teja:感谢您指出,但更改也将在if语句中进行。 - Vishal
有时在一个“增长”的文件夹上(另一个线程正在下载文件和文件夹,同时我正在打印文件夹大小作为进展),它会在“for (File file:dir.listFiles()) {”这一行抛出nullpointerexception异常。一些文件在活动文件夹上很快地出现和消失。因此,在for循环之前,我添加了对*dir.listFiles()*返回值的空检查。 - csonuryilmaz
从File.listFiles() javadoc中:如果目录为空,则该数组将为空。如果此抽象路径名不表示目录,或者发生I/O错误,则返回null。因此,在获取动态更改的文件夹大小时,上述注释非常有用。 - csonuryilmaz

8

对于Java 8,这是正确的做法:

Files.walk(new File("D:/temp").toPath())
                .map(f -> f.toFile())
                .filter(f -> f.isFile())
                .mapToLong(f -> f.length()).sum()
过滤掉所有的目录非常重要,因为长度方法并不能保证对于目录来说长度一定是0。

至少这段代码提供了与Windows资源管理器相同的文件大小信息。


5

以下是获取一般文件大小的最佳方法(适用于目录和非目录):

public static long getSize(File file) {
    long size;
    if (file.isDirectory()) {
        size = 0;
        for (File child : file.listFiles()) {
            size += getSize(child);
        }
    } else {
        size = file.length();
    }
    return size;
}

编辑:请注意这可能是一个耗时的操作,不要在UI线程上运行。

此外,在这里(摘自https://dev59.com/33A75IYBdhLWcg3wfpKr#5599842)有一种很好的方法来从返回的长整型中获取用户可读的字符串:

public static String getReadableSize(long size) {
    if(size <= 0) return "0";
    final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
    int digitGroups = (int) (Math.log10(size)/Math.log10(1024));
    return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))
            + " " + units[digitGroups];
}

4

File.length() (Javadoc).

请注意,此方法不能用于目录,或者不能保证能正常工作。

对于目录,您想要什么?如果是其下所有文件的总大小,您可以使用File.list()File.isDirectory()递归遍历子目录并累加它们的大小。


4

File对象有一个length方法:

f = new File("your/file/name");
f.length();

4
如果您想使用Java 8 NIO API,以下程序将打印所在目录的大小(以字节为单位)。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathSize {

    public static void main(String[] args) {
        Path path = Paths.get(".");
        long size = calculateSize(path);
        System.out.println(size);
    }

    /**
     * Returns the size, in bytes, of the specified <tt>path</tt>. If the given
     * path is a regular file, trivially its size is returned. Else the path is
     * a directory and its contents are recursively explored, returning the
     * total sum of all files within the directory.
     * <p>
     * If an I/O exception occurs, it is suppressed within this method and
     * <tt>0</tt> is returned as the size of the specified <tt>path</tt>.
     * 
     * @param path path whose size is to be returned
     * @return size of the specified path
     */
    public static long calculateSize(Path path) {
        try {
            if (Files.isRegularFile(path)) {
                return Files.size(path);
            }

            return Files.list(path).mapToLong(PathSize::calculateSize).sum();
        } catch (IOException e) {
            return 0L;
        }
    }

}

calculateSize方法对于Path对象是通用的,因此也适用于文件。 注意,如果文件或目录无法访问,则路径对象返回的大小将为0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接