How do I convert the size of a file in bytes to KB, MB, GB, TB, etc?

2

I'm getting the file size to display on the screen:

new FileInfo(arquivo).Length

This property returns me the total bytes ... I would like to display in a format that I can understand, for example, instead of 2147483647 I want to show 2 GB .

How can I do this?

    
asked by anonymous 27.12.2013 / 18:16

2 answers

1

There are several ways to do this. One that caught my eye was the deepee1 method . I made a small adjustment and put this method into a extension methods class.

public static partial class NumericExtender
{
    private static IList<string> fileSizeUnits;

    static NumericExtender()
    {
        fileSizeUnits =
            new List<string>() { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
    }

    /// <summary>
    /// Retorna o valor formatado como tamanho de arquivo (KB, MB, GB...).
    /// </summary>
    /// <param name="totalBytes">Total de bytes do arquivo.</param>
    /// <param name="precision">Número de casas decimais para exibir.</param>
    /// <returns>Tamanho do arquivo formatado.</returns>
    public static string ToFileSize(this double totalBytes, int precision = 2)
    {
        if (totalBytes <= 0)
            return String.Format("0 {0}", fileSizeUnits[0]);

        double bytes = Math.Abs(totalBytes);
        int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
        if (place > fileSizeUnits.Count - 1)
            place = fileSizeUnits.Count - 1;

        double num = Math.Round(bytes / Math.Pow(1024, place), precision);
        return String.Format("{0} {1}", Math.Sign(totalBytes) * num, fileSizeUnits[place]);
    }
}

And I did a test method:

[TestMethod]
public void ToFileSize()
{
    Assert.AreEqual("0 B", Int32.MinValue.ToFileSize());
    Assert.AreEqual("0 B", UInt32.MinValue.ToFileSize());
    Assert.AreEqual("0 B", Int64.MinValue.ToFileSize());
    Assert.AreEqual("0 B", UInt64.MinValue.ToFileSize());
    Assert.AreEqual("0 B", Double.MinValue.ToFileSize());

    Assert.AreEqual("2 GB", Int32.MaxValue.ToFileSize());
    Assert.AreEqual("4 GB", UInt32.MaxValue.ToFileSize());
    Assert.AreEqual("8 EB", Int64.MaxValue.ToFileSize());
    Assert.AreEqual("16 EB", UInt64.MaxValue.ToFileSize());
    Assert.AreEqual("1.48701690847778E+284 YB", Double.MaxValue.ToFileSize());

    Assert.AreEqual("1.24 KB", 1270.ToFileSize());
    Assert.AreEqual("1.24023 KB", 1270.ToFileSize(5));
}
    
27.12.2013 / 18:16
10

Performance!

For those of you who need this formatting to perform well, just use constants and

)

public static string TamanhoAmigavel(long bytes)
{
    if (bytes < 0) throw new ArgumentException("bytes");

    double humano;
    string sufixo;

    if (bytes >= 1152921504606846976L) // Exabyte (1024^6)
    {
        humano = bytes >> 50;
        sufixo = "EB";
    }
    else if (bytes >= 1125899906842624L) // Petabyte (1024^5)
    {
        humano = bytes >> 40;
        sufixo = "PB";
    }
    else if (bytes >= 1099511627776L) // Terabyte (1024^4)
    {
        humano = bytes >> 30;
        sufixo = "TB";
    }
    else if (bytes >= 1073741824) // Gigabyte (1024^3)
    {
        humano = bytes >> 20;
        sufixo = "GB";
    }
    else if (bytes >= 1048576) // Megabyte (1024^2)
    {
        humano = bytes >> 10;
        sufixo = "MB";
    }
    else if (bytes >= 1024) // Kilobyte (1024^1)
    {
        humano = bytes;
        sufixo = "KB";
    }
    else return bytes.ToString("0 B"); // Byte

    humano /= 1024;
    return humano.ToString("0.## ") + sufixo;
}

Why the performance gain?

  • Using constants avoids the runtime calculation of the binary prefixes .
  • The operation of bit shift is, theoretically *, less costly to the processor than ordinary mathematical operations:
  

On simple low-cost processors, typically, bitwise operations are substantially faster than division, several times faster than multiplication, and sometimes significantly faster than addition. While modern processors usually perform addition and multiplication just as fast as bitwise operations due to their longer instruction pipelines and other architectural design choices, bitwise operations of commonly used less power because of the reduced use of resources.   ( source )

Translating:

  

In low-cost processors, bit operations are often substantially faster than division, often faster than multiplication, and sometimes faster than addition. Although modern processors typically do addition and multiplication as quickly as bit operations because of longer instruction queues and other architecture decisions, bit operations typically use less energy because they require a reduced amount of resources. >

* Theoretically because in practice the compiler can optimize your code.

In the question of the answer quoted by @BrunoLM there is an other interesting solution using the DLL Shlwapi

[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize(long fileSize, [MarshalAs(UnmanagedType.LPTStr)]StringBuilder buffer, int bufferSize);

public static string StrFormatByteSize(long filesize)
{
    StringBuilder sb = new StringBuilder(11);
    StrFormatByteSize(filesize, sb, sb.Capacity);
    return sb.ToString();
}

In most cases it would be an exaggeration to import an external DLL for this function only. But probably (does anyone confirm?) The formatting made by this function is exactly the one used by Windows Explorer; in some scenario this consistency may be useful or even necessary.

    
30.12.2013 / 15:02