Format file size as MB, GB, etc

104,414

Solution 1

public static String readableFileSize(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];
}

This will work up to 1000 TB.... and the program is short!

Solution 2

You'll probably have more luck with java.text.DecimalFormat. This code should probably do it (just winging it though...)

new DecimalFormat("#,##0.#").format(value) + " " + unit

Solution 3

It is surprising for me, but a loop-based algorithm is about 10% faster.

public static String toNumInUnits(long bytes) {
    int u = 0;
    for ( ; bytes > 1024*1024; bytes >>= 10) {
        u++;
    }
    if (bytes > 1024)
        u++;
    return String.format("%.1f %cB", bytes/1024f, " kMGTPE".charAt(u));
}
Share:
104,414
Sean Patrick Floyd
Author by

Sean Patrick Floyd

About me: Austrian / American Java developer / architect with 25+ years of experience, working in the Seattle Area for Domino Data Lab (views are my own). 50 y/o, married to a lovely German wife and father of three. Hold a 5th degree black belt in Taekwondo (Kukkiwon), love Monty Python and appreciate a good glass of Whisk(e)y. He / him. Java aficionado, dabbling in Scala / Kotlin / Groovy, off and on also JS, and Python or GoLang when I have to. Connect: Twitter, LinkedIn

Updated on August 13, 2020

Comments

  • Sean Patrick Floyd
    Sean Patrick Floyd almost 4 years

    I need to display a file size as a string using sensible units.

    For example,

    1L ==> "1 B";
    1024L ==> "1 KB";
    2537253L ==> "2.3 MB"
    

    etc.

    I found this previous answer, which I didn't find satisfactory.

    I have come up with my own solution which has similar shortcomings:

    private static final long K = 1024;
    private static final long M = K * K;
    private static final long G = M * K;
    private static final long T = G * K;
    
    public static String convertToStringRepresentation(final long value){
        final long[] dividers = new long[] { T, G, M, K, 1 };
        final String[] units = new String[] { "TB", "GB", "MB", "KB", "B" };
        if(value < 1)
            throw new IllegalArgumentException("Invalid file size: " + value);
        String result = null;
        for(int i = 0; i < dividers.length; i++){
            final long divider = dividers[i];
            if(value >= divider){
                result = format(value, divider, units[i]);
                break;
            }
        }
        return result;
    }
    
    private static String format(final long value,
        final long divider,
        final String unit){
        final double result =
            divider > 1 ? (double) value / (double) divider : (double) value;
        return String.format("%.1f %s", Double.valueOf(result), unit);
    }
    

    The main problem is my limited knowledge of Decimalformat and / or String.format. I would like 1024L, 1025L, etc. to map to 1 KB rather than 1.0 KB.

    So, two possibilities:

    1. I would prefer a good out-of-the-box solution in a public library like Apache Commons or Google Guava.
    2. If there isn't, how can I get rid of the '.0' part (without resorting to string replacement and regex, I can do that myself)?
  • Stefan Hoth
    Stefan Hoth almost 13 years
    This should be the "correct" answer as it takes the unit into account.
  • ruslanys
    ruslanys almost 13 years
    Agree, should be the correct answer. Look here for an even better output.
  • Joe
    Joe almost 12 years
    final String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "EB" }; // now it works up to Long.MAX_VALUE!
  • Mr Ed
    Mr Ed almost 12 years
    To comply with international standards: final String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; /* used with 1000 * / final String[] units = new String[] { "Bi", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" }; /* used with 1024 */ See: physics.nist.gov/cuu/Units/binary.html
  • nefo_x
    nefo_x over 10 years
    and reverse function?...
  • Mr Ed
    Mr Ed over 10 years
    nefo_x: The reverse function is impossible because you lose precision. If you wanted an approximation, you can parse the string to get the number part, then multiply that number by the factor which corresponds with the units string, i.e. "KB" would use the factor 1000.
  • Evgeni Sergeev
    Evgeni Sergeev almost 10 years
    Everything is loop based at the bottom.
  • Willem Van Onsem
    Willem Van Onsem over 9 years
    A small comment: "kilo" is expressed with a lowercase 'k' and not an uppercase 'K'.
  • dbro
    dbro about 8 years
    Might want to return "0 B" or otherwise include units in zero case.
  • user4955663
    user4955663 over 7 years
    Joe: Petabyte comes before Exabyte and don't forget these: Zettabyte, Yottabyte, so the complete list is: "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" See: en.wikipedia.org/wiki/Yottabyte Yes I know ZB and YB are not needed today, but maybe in the future...;)
  • Himanshu Mori
    Himanshu Mori over 7 years
    "This will work up to 1000 TB" yes it is not working after this limit
  • Chop
    Chop about 7 years
    Maybe using .format(size/Math.pow(1024, Math.min(digitGroups, units.length))) could fix the problem of filesizes above 1000TB?
  • Kamil Ibadov
    Kamil Ibadov almost 7 years
    Perfect, Thank you!
  • Erik B
    Erik B almost 6 years
    @WillemVanOnsem kilo is 1000, this is 1024
  • Willem Van Onsem
    Willem Van Onsem almost 6 years
    @ErikB: I did not post this, I only replaced KB with kB :)
  • android developer
    android developer over 5 years
    Just a tip for Android developers : To show the file size in a nice readable way on Android, you can use Formatter.formatShortFileSize(context,file.length())
  • user4955663
    user4955663 over 5 years
    See scala solution ported from this code: stackoverflow.com/questions/35609587/… Also support for ZB and YB.
  • Anupreet Kaur
    Anupreet Kaur about 4 years
    Getting crash sometimes. Usually when its in KB java.lang.NumberFormatException: For input string: "1,010.3" @WillemVanOnsem Can you please help me
  • Denis K
    Denis K almost 4 years
    This DecimalFormat will produce different results based on Locale. I added this to have consistency regardless of environment: DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); DecimalFormat formatter = new DecimalFormat("#,##0.##", symbols);
  • Peter Mortensen
    Peter Mortensen almost 4 years
    Does it work as required?
  • Peter Mortensen
    Peter Mortensen almost 4 years
    @Evgeni Sergeev: What do you mean? Can you elaborate?
  • Robert Watkins
    Robert Watkins almost 4 years
    Strangely enough, after ten years, I haven't gone back and tested this. Try it yourself and see...
  • xonya
    xonya over 3 years
    Warning: this doesn't work for bytes <= 1024 (result is always 0.0 B). You need to add if (bytes <= 1024) { return String.format("%.1f B", (float) bytes); } at the beginning of the method.