在Android中,如何获取SD卡上文件夹的大小?

35

在SD卡上轻松获取文件夹的大小是否可能?我使用一个文件夹来缓存图像,并希望呈现所有缓存图像的总大小。除了迭代每个文件之外,是否还有其他方法可以做到这一点?它们都驻留在同一个文件夹中吗?

11个回答

45

只需遍历所有文件并将它们的长度相加:

/**
 * Return the size of a directory in bytes
 */
private static long dirSize(File dir) {

    if (dir.exists()) {
        long result = 0;
        File[] fileList = dir.listFiles();
        if (fileList != null) {
            for(int i = 0; i < fileList.length; i++) {
                // Recursive call if it's a directory
                if(fileList[i].isDirectory()) {
                    result += dirSize(fileList[i]);
                } else {
                    // Sum the file size in bytes
                    result += fileList[i].length();
                }
            }
        }
        return result; // return the file size
    }
    return 0;
}

注意:此函数是手写的,因此无法编译!


你可能想用dirSize代替findFile :) - Maurits Rijk
我建议将 dir.exists() 替换为 dir.isDirectory()。如果将文件作为参数传递,由于 listFiles() 的结果会抛出 NullPointerException。 - tux_mind
@Moss "for-each" 循环更好,就像谷歌在http://developer.android.com/training/articles/perf-tips.html#Loops中建议的那样。 - Kevin Robatel
在对fileList进行循环之前,请检查其是否为null。 - mehrdad seyrafi

20

这是避免递归的一些代码:

public static long getFileSize(final File file) {
    if (file == null || !file.exists())
        return 0;
    if (!file.isDirectory())
        return file.length();
    final List<File> dirs = new LinkedList<>();
    dirs.add(file);
    long result = 0;
    while (!dirs.isEmpty()) {
        final File dir = dirs.remove(0);
        if (!dir.exists())
            continue;
        final File[] listFiles = dir.listFiles();
        if (listFiles == null || listFiles.length == 0)
            continue;
        for (final File child : listFiles) {
            // Note: if you want to get physical size and not just logical size, include directories too to the result, and not just normal files
            if (child.isDirectory()) {
                dirs.add(child);
            } else {
                result += child.length();
            }
        }
    }
    return result;
}

@androiddeveloper 这是扇区大小。你会发现在任何桌面操作系统上也是如此。 - You'reAGitForNotUsingGit
@AndroidDev 好的。谢谢。 - android developer
该值与三星我的文件应用程序中所选文件夹中报告的详细信息相匹配。 - ecle
@androiddeveloper 是的,一个文件夹中每个文件的总大小(当然不包括文件夹本身)。 - ecle
@ecle 我明白了,谢谢。 - android developer
显示剩余10条评论

7

你应该使用以下代码:

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

对我来说是个很好的解决方案,我有一个文件夹里面有一些音频文件,这对我非常完美!(在这个文件夹中我没有子文件夹!) - basti12354

6
/**
 * Try this one for better performance
 * Mehran
 * Return the size of a directory in bytes
 **/

private static long dirSize(File dir) {
    long result = 0;

    Stack<File> dirlist= new Stack<File>();
    dirlist.clear();

    dirlist.push(dir);

    while(!dirlist.isEmpty())
    {
        File dirCurrent = dirlist.pop();

        File[] fileList = dirCurrent.listFiles();
        for(File f: fileList){
            if(f.isDirectory())
                dirlist.push(f);
            else
                result += f.length();
        }
    }

    return result;
}

2
由于我们正在讨论文件操作,递归不太可能对性能造成太大影响。此外,java.util.Stack实现非常慢。我尝试使用它来优化递归算法,但实际上比让JVM自己处理更慢。 - Kevin Coulombe
java.util.Stack类的方法是同步的。如果你真的想避免递归,最好使用LinkedList。 - Roman Mazur

4

#Moss的方法是正确的。以下是我提供的代码,用于将字节转换为易读的格式。您只需将文件夹路径赋值给dirSize(String path),即可根据字节、千字节、兆字节等获取易读的格式。

private static String dirSize(String path) {

        File dir = new File(path);

        if(dir.exists()) {
            long bytes = getFolderSize(dir);
            if (bytes < 1024) return bytes + " B";
            int exp = (int) (Math.log(bytes) / Math.log(1024));
            String pre = ("KMGTPE").charAt(exp-1) + "";

            return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
        }

        return "0";
    }

    public static long getFolderSize(File dir) {
        if (dir.exists()) {
            long result = 0;
            File[] fileList = dir.listFiles();
            for(int i = 0; i < fileList.length; i++) {
                // Recursive call if it's a directory
                if(fileList[i].isDirectory()) {
                    result += getFolderSize(fileList[i]);
                } else {
                    // Sum the file size in bytes
                    result += fileList[i].length();
                }
            }
            return result; // return the file size
        }
        return 0;
    } 

这是关于可读性的另一个问题,所以回答有些离题了。 - user25

4
其他解决方案的问题在于它们只提供指定目录中所有文件的逻辑大小。这将与实际(物理)使用空间不同。如果您的目录有很多子目录和/或小文件,则目录的逻辑大小和实际大小可能存在巨大差异。
以下是我发现如何考虑FS的物理结构。
public static long getDirectorySize(File directory, long blockSize) {
    File[] files = directory.listFiles();
    if (files != null) {

        // space used by directory itself 
        long size = file.length();

        for (File file : files) {
            if (file.isDirectory()) {
                // space used by subdirectory
                size += getDirectorySize(file, blockSize);
            } else {
                // file size need to rounded up to full block sizes
                // (not a perfect function, it adds additional block to 0 sized files
                // and file who perfectly fill their blocks) 
                size += (file.length() / blockSize + 1) * blockSize;
            }
        }
        return size;
    } else {
        return 0;
    }
}

你可以使用 StatFs 来获取块大小:
public static long getDirectorySize(File directory) {
    StatFs statFs = new StatFs(directory.getAbsolutePath());
    long blockSize;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        blockSize = statFs.getBlockSizeLong()
    } else {
        blockSize = statFs.getBlockSize();
    }

    return getDirectorySize(directory, blockSize);
}

我注意到如果我在目录上调用“length()”,我得到的不是0,而是一个实数。你觉得有可能直接在目录上使用“length()”(当然还要加上普通文件的大小)来代替你所做的事情吗? - android developer
你的意思是想让 return getDirectorySize(directory, blockSize); 只返回 return blockSize 吗? - Joshua Pinter
非常感谢您提供了这个答案。这是我们第一次接触 StatFs,正是我们所需要的。对于大型和复杂的目录,它比递归获取文件大小快1000倍以上。太棒了! - Joshua Pinter

4
您可以查询MediaStore以获取内部存储上目录的大小。这比递归方法获取目录中每个文件的长度要快得多。您必须被授予READ_EXTERNAL_STORAGE权限。 示例:
/**
 * Query the media store for a directory size
 *
 * @param context
 *     the application context
 * @param file
 *     the directory on primary storage
 * @return the size of the directory
 */
public static long getFolderSize(Context context, File file) {
  File directory = readlink(file); // resolve symlinks to internal storage
  String path = directory.getAbsolutePath();
  Cursor cursor = null;
  long size = 0;
  try {
    cursor = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),
        new String[]{MediaStore.MediaColumns.SIZE},
        MediaStore.MediaColumns.DATA + " LIKE ?",
        new String[]{path + "/%"},
        null);
    if (cursor != null && cursor.moveToFirst()) {
      do {
        size += cursor.getLong(0);
      } while (cursor.moveToNext());
    }
  } finally {
    if (cursor != null) {
      cursor.close();
    }
  }
  return size;
}

/**
 * Canonicalize by following all symlinks. Same as "readlink -f file".
 *
 * @param file
 *     a {@link File}
 * @return The absolute canonical file
 */
public static File readlink(File file) {
  File f;
  try {
    f = file.getCanonicalFile();
  } catch (IOException e) {
    return file;
  }
  if (f.getAbsolutePath().equals(file.getAbsolutePath())) {
    return f;
  }
  return readlink(f);
}

使用方法:

File DCIM = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
long directorySize = getFolderSize(context, DCIM);
String formattedSize = Formatter.formatFileSize(context, directorySize);
System.out.println(DCIM + " " + formattedSize);

输出:

/storage/emulated/0/DCIM 30.86 MB


是的,如果谈论媒体文件,这是最好的解决方案。 - user924
"/%/%"是错误的,如果您不想排除根文件夹中的文件,则应该使用"/%",使用您的解决方案"/%/%"将仅获取该根文件夹中子文件夹的大小。getFolderSize(context, folderRoot); - user924
@user924,请不要在没有参考资料的情况下误导他人。媒体提供者表格包含了媒体存储中所有文件的索引,包括非媒体文件。 https://developer.android.com/reference/android/provider/MediaStore.Files - Cyber Avater

2
以下方法将返回文件夹的大小:
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;
}

调用上述方法:

File file = new File(Environment.getExternalStorageDirectory().getPath()+"/urfoldername/");

long folder_size=getFolderSize(file);

返回文件夹的大小。


1
希望这能有所帮助

import this


import android.text.format.Formatter;

文件大小
public static String fileSize(File file, Context context) {
        return Formatter.formatFileSize(context, file.length());
    }

关于文件夹大小

 public static String forlderSize(File file, Context context) {
        long length = 0;
        File[] folderFiles = file.listFiles();
        for (File f : folderFiles) {
            length += f.length();
        }

        return Formatter.formatFileSize(context, length);
    }

0
这是Linh Toòng答案中的一小段代码片段,其中包含了几个额外的检查(主要是为了防止Android Studio的警告!)
private long getFolderSize(File file) {

        if (file == null || !file.exists())
            return 0;

        long size = 0;

        if (file.isDirectory()) {

            File[] files = file.listFiles();

            if (files == null || files.length == 0)
                return size;

            for (File f : files)
                size += getFolderSize(f);

        } else
            size += file.length();

        return size;

    }

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