33

Is it possible to easily get the size of a folder on the SD card? I use a folder for caching of images, and would like to present the total size of all cached images. Is there a way to this other than iterating over each file? They all reside inside the same folder?

Gunnar Lium
  • 5,853
  • 9
  • 34
  • 33

11 Answers11

45

Just go through all files and sum the length of them:

/**
 * 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;
}

NOTE: Function written by hand so it could not compile!

Moss
  • 6,002
  • 1
  • 34
  • 40
20

Here's some code that avoids recursion, and also calculates the physical size instead of the logical size:

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) {
            result += child.length();
            if (child.isDirectory())
                dirs.add(child);
        }
    }
    return result;
}
Kedar Tendolkar
  • 466
  • 3
  • 9
android developer
  • 112,189
  • 137
  • 688
  • 1,196
6

you should use this code:

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;
}
faceman
  • 1,300
  • 11
  • 20
Linh Toòng
  • 61
  • 1
  • 1
  • Great solution for me, I am having a folder with some audio files and it works perfekt for me! (I am having no subfolders in this folder!) – basti12354 Apr 04 '15 at 15:17
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;
}
Pointer Null
  • 38,378
  • 13
  • 83
  • 109
Mehran
  • 61
  • 1
  • 1
  • 2
    Since we're talking about file operations, the recursion is unlikely to account for much of the performance hit. Also, the java.util.Stack implementation is very slow. I tried to optimize a recursive algorithm with it and it was actually slower then to let the JVM do its job. – Kevin Coulombe Dec 12 '11 at 05:21
  • java.util.Stack class methods are synchronized. If you really want to avoid recursion it's better to use LinkedList. – Roman Mazur Feb 12 '13 at 14:06
4

The way of #Moss is right. This is my code for those who want to change bytes to human readable format. You just need to assign path of your folder to dirSize(String path) and get human readable format based on byte, kilo, mega and etc.

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;
    } 
Hesam
  • 49,622
  • 70
  • 212
  • 354
4

Problem with other solution is that they provide you only logical size of all files in specified directory. It will be different from actual (physical) used space. If your directory has a lot of subdirectories and/or small files, there may be a huge difference between logical and actual size of directory.

Here is what I found how to take in count physical structure of 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;
    }
}

You can use StatFs to get block size:

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);
}
Aleksejs Mjaliks
  • 8,467
  • 5
  • 37
  • 44
  • I've noticed that if I call "length()" on a directory, I don't get 0, but a real number. Is it possible that instead of using what you've done you can just use "length()" on the directories (and of course do the rest- adding the size of normal files) ? – android developer Apr 30 '14 at 22:10
  • Did you mean for `return getDirectorySize(directory, blockSize);` to just be `return blockSize`? – Joshua Pinter Oct 15 '19 at 03:57
  • Thank you so much for providing this answer. This was our first introduction to `StatFs` and was exactly what we needed. On large and complex directories, it was over 1000x faster than recursively getting the file size. Brilliant! – Joshua Pinter Oct 16 '19 at 20:41
3

You can query MediaStore for a directory size on internal storage. This is much faster than a recursive method getting the length of each file in a directory. You must have READ_EXTERNAL_STORAGE permission granted.

Example:

/**
 * 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);
}

Usage:

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

Output:

/storage/emulated/0/DCIM 30.86 MB

user924
  • 5,286
  • 3
  • 35
  • 97
Jared Rummler
  • 36,896
  • 19
  • 133
  • 145
  • yeah, if talking about media files, this is the best solution – user924 Nov 01 '20 at 17:34
  • `"/%/%"` - is wrong, it should be `/%` if you don't want to exclude files in root folder - `getFolderSize(context, folderRoot);`, with your solution `"/%/%"` it would get size only of subfolders in that root folder – user924 Dec 21 '20 at 14:12
2

below method return you size of folder:-

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;
}

call above method :-

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

long folder_size=getFolderSize(file);

return you size of folder.

duggu
  • 37,191
  • 12
  • 114
  • 111
1

Hope this helps

import this

import android.text.format.Formatter;

For file size

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

For folder size

 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);
    }
zaai
  • 51
  • 1
0

Iterating through all files is less than 5 lines of code and the only reasonable way to do this. If you want to get ugly you could also run a system command (Runtime.getRuntime().exec("du");) and catch the output ;)

Maurits Rijk
  • 9,495
  • 2
  • 33
  • 53
  • 1
    Fair enough. Just figured it was such a common use case that there should be some native solution. Laziness is good ... Five lines later, and I'm happy :) – Gunnar Lium Oct 28 '10 at 08:38
  • In Clojure: (defn dir-size [dir] (reduce + (map #(.length %) (.listFiles (new File dir))))) – Maurits Rijk Oct 28 '10 at 12:19
  • 1
    I don't think it's safe to rely on du being available and executable. – David Snabel-Caunt Mar 14 '11 at 11:04
  • How exactly does one fire the "du" command? I tried - `Runtime.getRuntime().exec("/system/bin/du -b -d1 "+dir.getCanonicalPath(), new String[]{}, Environment.getRootDirectory());` didnt work. Nor did - `(Runtime.getRuntime().exec("du"))` – Amey Jul 07 '11 at 19:53
0

Here's a snippet from Linh Toòng's answer with a couple more checks (mainly to stop Android Studio's warnings!)

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;

    }
Vishnu
  • 633
  • 3
  • 7
  • 22