获取文件夹或文件的大小


105

如何在Java中检索文件夹或文件的大小?



如果您碰巧在Android上,请查看StatFs。它使用文件系统统计信息,比递归方法快1000倍,而递归方法无法满足我们的需求。我们的实现可以在这里找到:stackoverflow.com/a/58418639/293280
Joshua Pinter19年

Answers:


191
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子句,以检查File实际上是否是目录。
Paul Clapham 2010年

2
简单的更改即可检查参数,以查看方法开头是否不是目录,然后返回长度,然后递归就更简单-只需在同一方法中添加对self的调用,然后就支持传递文件引用了目录。
凯文·布洛克

3
如果您使用的是Java 7或更高版本,请使用stackoverflow.com/a/19877372/40064的答案,它的速度要快得多。
Wim Deblauwe 2015年

1
这将被符号链接混淆。此外,NullPointerException如果同时修改目录,则可能会引发。
Aleksandr Dubinsky

43

使用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();
}

在Android开发上有与此等效的功能吗?
android开发人员

有使用AtomicLong而不是仅仅使用的理由long吗?
卢卡斯·施梅尔盖森2014年

从匿名类访问时,该变量必须为final
Aksel Willgert 2014年

1
我使用JMH进行了基准测试,这种NIO api方法比commons-io代码(在包含许多子文件夹的文件夹中进行测试,总共180229个文件)进行测试的速度快4到5倍。Commons IO花费了15秒,NIO花费了5秒。
Wim Deblauwe

3
这种方法是最可靠的。它处理符号链接,并发修改,安全异常,可同时用于文件和目录等。可惜的是,它Files不直接支持它!
Aleksandr Dubinsky

38

你需要FileUtils#sizeOfDirectory(File)公共-io的

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

警告:此方法(从commons-io 2.4开始)存在一个错误,IllegalArgumentException如果同时修改目录,则可能会抛出该错误。


那么,当文件不是目录时会发生什么呢?什么时候不存在?等等-多么糟糕的文档
Mr_and_Mrs_D 2013年

@Mr_and_Mrs_D-只需在checkDirectory(directory);检查后复制并粘贴行。只要确保File.listFiles有孩子。参考: FileUtils.sizeOfDirectory()
File.listFiles

3
请参阅错误IO-449IllegalArgumentException如果在迭代过程中修改了目录,则此方法将引发。
Aleksandr Dubinsky

哎哟!!!太烂了,是的,它列出了文件,然后如果在代码运行时删除了文件,则会抛出该文件。
迪恩·希勒

19

在Java 8中:

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

Files::size在map步骤中使用会更好,但是会抛出一个已检查的异常。

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


1
我一直在研究类似的东西,最后遇到了有问题的代码:stackoverflow.com/questions/22867286/…,如您所见,错误处理的另一方面也导致了问题。
Aksel Willgert 2014年

@AkselWillgert谢谢,这很不幸,我已经更新了答案。现在切换到番石榴stackoverflow.com/a/24757556/1180621
Andrejs 2014年

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 2010年

有时在不断增长的文件夹上(另一个线程正在下载文件和文件夹,同时我在打印正在打印的文件夹大小)时,它在行“ for(File file:dir.listFiles()){”处给出nullpointerexception。一些文件在活动文件夹中显示并快速消失。因此,在for循环之前,我为dir.listFiles()返回值添加了空检查。
csonuryilmaz

从File.listFiles()javadoc:“如果目录为空,则数组为空。如果此抽象路径名不表示目录,或者发生I / O错误,则返回null。” 因此,在动态更改文件夹上获取文件夹大小时,以上注释非常有用。
csonuryilmaz

7

对于Java 8,这是一种正确的方法:

Files.walk(new File("D:/temp").toPath())
                .map(f -> f.toFile())
                .filter(f -> f.isFile())
                .mapToLong(f -> f.length()).sum()

过滤掉所有目录非常重要,因为不能保证将length方法用于目录为0。

至少此代码提供与Windows资源管理器本身相同的大小信息。


4

这是获取常规文件大小的最佳方法(适用于目录和非目录):

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://stackoverflow.com/a/5599842/1696171)是一种从长返回中获取用户可读字符串的好方法:

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

如果要使用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


3

File.length()Javadoc)。

请注意,这不适用于目录,或不能保证有效。

对于目录,您想要什么?如果它是其下所有文件的总大小,则可以递归地使用File.list()和汇总孩子File.isDirectory()的大小。


3

File对象具有一个length方法:

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

3
  • 适用于AndroidJava
  • 适用于文件夹和文件
  • 在需要的任何地方检查空指针
  • 忽略符号链接或快捷方式
  • 准备生产!

源代码:

   public long fileSize(File root) {
        if(root == null){
            return 0;
        }
        if(root.isFile()){
            return root.length();
        }
        try {
            if(isSymlink(root)){
                return 0;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }

        long length = 0;
        File[] files = root.listFiles();
        if(files == null){
            return 0;
        }
        for (File file : files) {
            length += fileSize(file);
        }

        return length;
    }

    private static boolean isSymlink(File file) throws IOException {
        File canon;
        if (file.getParent() == null) {
            canon = file;
        } else {
            File canonDir = file.getParentFile().getCanonicalFile();
            canon = new File(canonDir, file.getName());
        }
        return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
    }

1

对于Windows,使用java.io的此递归函数很有用。

    public static long folderSize(File directory) {
    long length = 0;

    if (directory.isFile())
         length += directory.length();
    else{
        for (File file : directory.listFiles()) {
             if (file.isFile())
                 length += file.length();
             else
                 length += folderSize(file);
        }
    }

    return length;
}

这已经过测试,并且可以正常工作。


1

我已经测试过,du -c <folderpath>并且比nio快2倍。

private static long getFolderSize(File folder){
  if (folder != null && folder.exists() && folder.canRead()){
    try {
      Process p = new ProcessBuilder("du","-c",folder.getAbsolutePath()).start();
      BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
      String total = "";
      for (String line; null != (line = r.readLine());)
        total = line;
      r.close();
      p.waitFor();
      if (total.length() > 0 && total.endsWith("total"))
        return Long.parseLong(total.split("\\s+")[0]) * 1024;
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
  return -1;
}

0
public long folderSize (String directory)
    {
        File curDir = new File(directory);
        long length = 0;
        for(File f : curDir.listFiles())
        {
            if(f.isDirectory())
            {               
                 for ( File child : f.listFiles()) 
                 {
                     length = length + child.length();
                 }

                System.out.println("Directory: " + f.getName() + " " + length + "kb");
            }
            else
            {
                length = f.length();
                System.out.println("File: " + f.getName() + " " + length + "kb");
            }
            length = 0;
        }
        return length;
    }

0

经过大量研究并研究了StackOverflow此处提出的不同解决方案。我最终决定编写自己的解决方案。我的目的是拥有无抛出机制,因为如果API无法获取文件夹大小,我不想崩溃。此方法不适用于多线程方案。

首先,我想在遍历文件系统树时检查有效目录。

private static boolean isValidDir(File dir){
    if (dir != null && dir.exists() && dir.isDirectory()){
        return true;
    }else{
        return false;
    }
}

其次,我不希望我的递归调用进入符号链接(软链接)并在总聚合中包括大小。

public static boolean isSymlink(File file) throws IOException {
    File canon;
    if (file.getParent() == null) {
        canon = file;
    } else {
        canon = new File(file.getParentFile().getCanonicalFile(),
                file.getName());
    }
    return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}

最后,我基于递归的实现获取指定目录的大小。注意dir.listFiles()的空检查。根据javadoc,此方法可能会返回null。

public static long getDirSize(File dir){
    if (!isValidDir(dir))
        return 0L;
    File[] files = dir.listFiles();
    //Guard for null pointer exception on files
    if (files == null){
        return 0L;
    }else{
        long size = 0L;
        for(File file : files){
            if (file.isFile()){
                size += file.length();
            }else{
                try{
                    if (!isSymlink(file)) size += getDirSize(file);
                }catch (IOException ioe){
                    //digest exception
                }
            }
        }
        return size;
    }
}

蛋糕上的奶油,API以获得列表文件的大小(可能是根目录下的所有文件和文件夹)。

public static long getDirSize(List<File> files){
    long size = 0L;
    for(File file : files){
        if (file.isDirectory()){
            size += getDirSize(file);
        } else {
            size += file.length();
        }
    }
    return size;
}

0

在Linux中,如果要对目录排序,则du -hs * | 排序-h


0

您可以使用Apache Commons IO轻松找到文件夹的大小。

如果您使用的是maven,请在pom.xml文件中添加以下依赖项。

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

如果不喜欢Maven,请下载以下jar,并将其添加到类路径中。

https://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar

public long getFolderSize() {

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

    return size; // in bytes
}

要通过Commons IO获取文件大小,

File file = new File("ADD YOUR PATH TO FILE");

long fileSize = FileUtils.sizeOf(file);

System.out.println(fileSize); // bytes

也可以通过以下方式实现 Google Guava

对于Maven,添加以下内容:

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>28.1-jre</version>
</dependency>

如果不使用Maven,则将以下内容添加到类路径中

https://repo1.maven.org/maven2/com/google/guava/guava/28.1-jre/guava-28.1-jre.jar

public long getFolderSizeViaGuava() {
        File folder = new File("src/test/resources");
        Iterable<File> files = Files.fileTreeTraverser()
                .breadthFirstTraversal(folder);
        long size = StreamSupport.stream(files.spliterator(), false)
                .filter(f -> f.isFile())
                .mapToLong(File::length).sum();

        return  size;
    }

要获取文件大小,

 File file = new File("PATH TO YOUR FILE");
 long s  = file.length();
 System.out.println(s);

0
private static long getFolderSize(Path folder) {
        try {
            return Files.walk(folder)
                      .filter(p -> p.toFile().isFile())
                      .mapToLong(p -> p.toFile().length())
                      .sum();
        } catch (IOException e) {
            e.printStackTrace();
            return 0L;
        }

尽管您的代码看起来不错,但我不确定它是否会为其他答案添加任何内容。如果是这样,请编辑您的答案以进行解释。
Dragonthoughts

它只是用更少的代码来完成相同工作的更新版本。
Jaskaran Singh,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.