Como obtenho um tamanho de arquivo legível por humanos na abreviação de bytes usando o .NET?

Respostas:

353

Essa não é a maneira mais eficiente de fazer isso, mas é mais fácil de ler se você não estiver familiarizado com a matemática de log e deve ser rápido o suficiente para a maioria dos cenários.

string[] sizes = { "B", "KB", "MB", "GB", "TB" };
double len = new FileInfo(filename).Length;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1) {
    order++;
    len = len/1024;
}

// Adjust the format string to your preferences. For example "{0:0.#}{1}" would
// show a single decimal place, and no space.
string result = String.Format("{0:0.##} {1}", len, sizes[order]);
David Thibault
fonte
12
Eu acredito que você poderia usar Math.Log para determinar a ordem em vez de usar um loop while.
precisa
12
Além disso, o KB tem 1000 bytes. 1024 bytes é KiB .
Constantin
12
@ Constant bem, isso depende do sistema operacional? Windows ainda conta 1024 bytes como 1 KB e 1 MB = 1024 KB, Pessoalmente eu quero jogar o KiB para fora da janela e apenas contar cada coisa usando 1024 ...?
Peter
4
@Petoj, não depende do sistema operacional, a definição é independente do sistema operacional. Da Wikipedia:The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
ANeves
3
Eu prefiro esse código, pois ele parece correr mais rápido, mas eu o modifiquei um pouco para permitir diferentes números de casas decimais. Números menores mostram melhor 2 casas decimais, por exemplo, 1,38 MB, enquanto que números maiores exigem menos casas decimais, por exemplo, 246k ou 23,5KB:
Myke Black
320

usando o Log para resolver o problema ....

static String BytesToString(long byteCount)
{
    string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
    if (byteCount == 0)
        return "0" + suf[0];
    long bytes = Math.Abs(byteCount);
    int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
    double num = Math.Round(bytes / Math.Pow(1024, place), 1);
    return (Math.Sign(byteCount) * num).ToString() + suf[place];
}

Também em c #, mas deve ser um piscar de olhos para converter. Também arredondado para uma casa decimal para facilitar a leitura.

Basicamente, determine o número de casas decimais na Base 1024 e, em seguida, divida por 1024 ^ casas decimais.

E algumas amostras de uso e saída:

Console.WriteLine(BytesToString(9223372036854775807));  //Results in 8EB
Console.WriteLine(BytesToString(0));                    //Results in 0B
Console.WriteLine(BytesToString(1024));                 //Results in 1KB
Console.WriteLine(BytesToString(2000000));              //Results in 1.9MB
Console.WriteLine(BytesToString(-9023372036854775807)); //Results in -7.8EB

Edit: Foi indicado que eu perdi um math.floor, então eu o incorporei. (Convert.ToInt32 usa arredondamento, não truncamento, e é por isso que Floor é necessário.) Obrigado pela captura.

Edit2: Houve alguns comentários sobre tamanhos negativos e tamanhos de 0 byte, então atualizei para lidar com esses 2 casos.

deepee1
fonte
7
Quero advertir que, embora essa resposta seja realmente um pequeno pedaço de código, ela não é a mais otimizada. Gostaria que você desse uma olhada no método postado por @humbads. Eu executei microtestes enviando 10.000.000 de tamanhos de arquivos gerados aleatoriamente através dos dois métodos, e isso mostra que o método dele é ~ 30% mais rápido. No entanto, eu fiz alguma limpeza adicional no método dele (atribuições desnecessárias e seleção de elenco). Além disso, eu executei um teste com um tamanho negativo (quando você está comparando arquivos), enquanto o método das humbads processa perfeitamente este método desse Log, lançando uma exceção!
precisa saber é o seguinte
1
Sim, você deve adicionar Math.Abs ​​para tamanhos negativos. Além disso, o código não lidar com o caso, se o tamanho é exatamente 0.
dasheddot
Math.Abs, Math.Floor, Math.Log, Convertendo para número inteiro, Math.Round, Math.Pow, Math.Sign, Adicionando, Multiplicando, Dividindo? Não foi essa quantidade de matemática que causou um grande aumento no processador. Este é provavelmente mais lento do que o código @humbads
Jayson Ragasa
Falha em double.MaxValue(place = 102)
BrunoLM
Funciona bem! Para imitar a maneira como o Windows funciona (pelo menos no meu Windows 7 Ultimate), substitua o Math.Round por Math.Ceiling. Obrigado novamente. Eu gosto desta solução.
H_He
101

Uma versão testada e significativamente otimizada da função solicitada é postada aqui:

Tamanho do arquivo legível por humanos em C # - Função otimizada

Código fonte:

// Returns the human-readable file size for an arbitrary, 64-bit file size 
// The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB"
public string GetBytesReadable(long i)
{
    // Get absolute value
    long absolute_i = (i < 0 ? -i : i);
    // Determine the suffix and readable value
    string suffix;
    double readable;
    if (absolute_i >= 0x1000000000000000) // Exabyte
    {
        suffix = "EB";
        readable = (i >> 50);
    }
    else if (absolute_i >= 0x4000000000000) // Petabyte
    {
        suffix = "PB";
        readable = (i >> 40);
    }
    else if (absolute_i >= 0x10000000000) // Terabyte
    {
        suffix = "TB";
        readable = (i >> 30);
    }
    else if (absolute_i >= 0x40000000) // Gigabyte
    {
        suffix = "GB";
        readable = (i >> 20);
    }
    else if (absolute_i >= 0x100000) // Megabyte
    {
        suffix = "MB";
        readable = (i >> 10);
    }
    else if (absolute_i >= 0x400) // Kilobyte
    {
        suffix = "KB";
        readable = i;
    }
    else
    {
        return i.ToString("0 B"); // Byte
    }
    // Divide by 1024 to get fractional value
    readable = (readable / 1024);
    // Return formatted number with suffix
    return readable.ToString("0.### ") + suffix;
}
humbads
fonte
1
+1! Mais simples e direto! Faz com que o processador faça as contas com facilidade e rapidez!
Jayson Ragasa
Para sua informação, você não usa o valor em double readable = (i < 0 ? -i : i);nenhum lugar, então remova-o. mais uma coisa, o elenco é redaundat
Royi Namir
Eu removi o elenco, adicionei comentários e corrigi um problema com o sinal negativo.
humbads
Ótima resposta. Obrigado, por que não usar Math.Abs?
kspearrin
1
(i <0? -i: i) é aproximadamente 15% mais rápido que Math.Abs. Para um milhão de chamadas, o Math.Abs ​​é 0,5 milissegundos mais lento na minha máquina - 3,2 ms vs 3,7 ms.
Humbads
72
[DllImport ( "Shlwapi.dll", CharSet = CharSet.Auto )]
public static extern long StrFormatByteSize ( 
        long fileSize
        , [MarshalAs ( UnmanagedType.LPTStr )] StringBuilder buffer
        , int bufferSize );


/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="filelength">The numeric value to be converted.</param>
/// <returns>the converted string</returns>
public static string StrFormatByteSize (long filesize) {
     StringBuilder sb = new StringBuilder( 11 );
     StrFormatByteSize( filesize, sb, sb.Capacity );
     return sb.ToString();
}

De: http://www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html

Prumo
fonte
36
Eu posso ser um noob, mas usar canhões gigantescos como pinvoke para matar aquele pato é um grande uso indevido.
24411 Bart
27
É isso que o explorer usa? Nesse caso, é magnificamente útil para permitir que as pessoas correspondam ao tamanho do arquivo que você mostra a elas com o que o Explorer mostra.
Andrew Backer,
8
E aquele que não reinventar a roda
Matthew Bloqueio
11 caracteres não são um limite constante e um pouco baixo para isso? Quero dizer, outros idiomas podem usar mais caracteres para o acrônimo de tamanho de byte ou outros estilos de formatação.
Ray
1
@Bart leva um tempo para os noobs aprenderem a sabedoria nisto: "Devemos esquecer pequenas eficiências, digamos cerca de 97% das vezes: a otimização prematura é a raiz de todo mal" ubiquity.acm.org/article.cfm? id = 1513451
Matthew Lock
22

Mais uma maneira de usá-lo, sem nenhum tipo de loop e com suporte a tamanho negativo (faz sentido para coisas como deltas de tamanho de arquivo):

public static class Format
{
    static string[] sizeSuffixes = {
        "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };

    public static string ByteSize(long size)
    {
        Debug.Assert(sizeSuffixes.Length > 0);

        const string formatTemplate = "{0}{1:0.#} {2}";

        if (size == 0)
        {
            return string.Format(formatTemplate, null, 0, sizeSuffixes[0]);
        }

        var absSize = Math.Abs((double)size);
        var fpPower = Math.Log(absSize, 1000);
        var intPower = (int)fpPower;
        var iUnit = intPower >= sizeSuffixes.Length
            ? sizeSuffixes.Length - 1
            : intPower;
        var normSize = absSize / Math.Pow(1000, iUnit);

        return string.Format(
            formatTemplate,
            size < 0 ? "-" : null, normSize, sizeSuffixes[iUnit]);
    }
}

E aqui está a suíte de testes:

[TestFixture] public class ByteSize
{
    [TestCase(0, Result="0 B")]
    [TestCase(1, Result = "1 B")]
    [TestCase(1000, Result = "1 KB")]
    [TestCase(1500000, Result = "1.5 MB")]
    [TestCase(-1000, Result = "-1 KB")]
    [TestCase(int.MaxValue, Result = "2.1 GB")]
    [TestCase(int.MinValue, Result = "-2.1 GB")]
    [TestCase(long.MaxValue, Result = "9.2 EB")]
    [TestCase(long.MinValue, Result = "-9.2 EB")]
    public string Format_byte_size(long size)
    {
        return Format.ByteSize(size);
    }
}
Constantin
fonte
19

Faça o checkout da biblioteca ByteSize . É System.TimeSpanpara bytes!

Ele lida com a conversão e formatação para você.

var maxFileSize = ByteSize.FromKiloBytes(10);
maxFileSize.Bytes;
maxFileSize.MegaBytes;
maxFileSize.GigaBytes;

Ele também faz representação e análise de cadeias.

// ToString
ByteSize.FromKiloBytes(1024).ToString(); // 1 MB
ByteSize.FromGigabytes(.5).ToString();   // 512 MB
ByteSize.FromGigabytes(1024).ToString(); // 1 TB

// Parsing
ByteSize.Parse("5b");
ByteSize.Parse("1.55B");
Omar
fonte
5
É a sua própria biblioteca, não?
Larsenal 13/03/2014
10
Não há vergonha em uma biblioteca útil como esta. :-)
Larsenal 13/03/2014
13

Eu gosto de usar o seguinte método (ele suporta até terabytes, o que é suficiente para a maioria dos casos, mas pode ser facilmente estendido):

private string GetSizeString(long length)
{
    long B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024;
    double size = length;
    string suffix = nameof(B);

    if (length >= TB) {
        size = Math.Round((double)length / TB, 2);
        suffix = nameof(TB);
    }
    else if (length >= GB) {
        size = Math.Round((double)length / GB, 2);
        suffix = nameof(GB);
    }
    else if (length >= MB) {
        size = Math.Round((double)length / MB, 2);
        suffix = nameof(MB);
    }
    else if (length >= KB) {
        size = Math.Round((double)length / KB, 2);
        suffix = nameof(KB);
    }

    return $"{size} {suffix}";
}

Lembre-se de que isso foi escrito para o C # 6.0 (2015), portanto, pode ser necessário um pouco de edição para versões anteriores.

Marca
fonte
11
int size = new FileInfo( filePath ).Length / 1024;
string humanKBSize = string.Format( "{0} KB", size );
string humanMBSize = string.Format( "{0} MB", size / 1024 );
string humanGBSize = string.Format( "{0} GB", size / 1024 / 1024 );
TcKs
fonte
Boa resposta. Deve haver um problema quando o tamanho do arquivo é muito pequeno; nesse caso / 1024 retorna 0. Você pode usar um tipo e chamada fracionários Math.Ceilingou algo assim.
Nawfal
10

Aqui está uma resposta concisa que determina a unidade automaticamente.

public static string ToBytesCount(this long bytes)
{
    int unit = 1024;
    string unitStr = "b";
    if (bytes < unit) return string.Format("{0} {1}", bytes, unitStr);
    else unitStr = unitStr.ToUpper();
    int exp = (int)(Math.Log(bytes) / Math.Log(unit));
    return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}

"b" é para bit, "B" é para Byte e "KMGTPEZY" são respectivamente para quilo, mega, giga, tera, peta, exa, zetta e yotta

Pode-se expandi-lo para levar em consideração a ISO / IEC80000 :

public static string ToBytesCount(this long bytes, bool isISO = true)
{
    int unit = 1024;
    string unitStr = "b";
    if (!isISO) unit = 1000;
    if (bytes < unit) return string.Format("{0} {1}", bytes, unitStr);
    else unitStr = unitStr.ToUpper();
    if (isISO) unitStr = "i" + unitStr;
    int exp = (int)(Math.Log(bytes) / Math.Log(unit));
    return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}
DKH
fonte
1
para todos que se perguntam por que existe um oKMGTPE posterior: byteé francês ( é octetem francês). Para qualquer outro idioma, substitua o oporb
Max R.
7
string[] suffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
int s = 0;
long size = fileInfo.Length;

while (size >= 1024)
{
    s++;
    size /= 1024;
}

string humanReadable = String.Format("{0} {1}", size, suffixes[s]);
bobwienholt
fonte
Você deve verificar: while (tamanho> = 1024 && s <sufixos.Comprimento).
TcKs
não ... um número inteiro assinado de 64 bits não pode ir além do ZB ... que representa os números 2 ^ 70.
11138 Bobwienholt
7
Então, por que colocar em YB?
Configurator
Eu mesmo gosto dessa resposta, mas todos aqui colocam soluções realmente ineficientes, você deve usar o deslocamento "size = size >> 10" muito mais rápido que a divisão ... e acho que é bom ter o do especificador grego extra de lá, porque no futuro próximo, uma função DLR posiable wouldnt precisa do "tamanho de tempo .." você poderia estar em uma CPU de 128 bits vector ou algo que pode segurar ZB e maior;)
RandomNickName42
4
O deslocamento de bits foi mais eficiente do que a divisão nos dias de codificação C no metal. Você fez um teste de desempenho no .NET para ver se o deslocamento de bits é realmente mais eficiente? Há pouco tempo, observei o estado do xor-swap e descobri que era realmente mais lento no .NET vs usando uma variável temp.
Pete
7

Se você estiver tentando corresponder ao tamanho, conforme mostrado na exibição de detalhes do Windows Explorer, este é o código que você deseja:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatKBSize(
    long qdw,
    [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf,
    int cchBuf);

public static string BytesToString(long byteCount)
{
    var sb = new StringBuilder(32);
    StrFormatKBSize(byteCount, sb, sb.Capacity);
    return sb.ToString();
}

Isso não apenas corresponderá exatamente ao Explorer, mas também fornecerá as seqüências traduzidas para você e as diferenças nas versões do Windows (por exemplo, no Win10, K = 1000 vs. versões anteriores K = 1024).

Metalogic
fonte
Esse código não é compilado, você precisa especificar a dll da qual a função veio. Portanto, o protótipo de toda a função se parece com: [DllImport ("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = true)] público estático externo long StrFormatKBSize (long qdw, [MarshalAs (UnmanagedType.LPTStr)] StringBuilder pszBuf, int cchBuf ); Deixe-me ser o primeiro a favorecer esta solução. Por que reinventar a roda se a roda já foi inventada? Essa é uma abordagem típica de todos os programadores de C #, mas infelizmente o C # não atinge todos os destinos atingidos pelo C ++.
TarmoPikaro
E mais uma correção de erro: Int64.MaxValue atinge 9.223.372.036.854.775.807, o que requer alocar o tamanho do buffer de 25+ - eu o arredondei para 32 apenas por precaução (não 11, como no código de demonstração acima).
TarmoPikaro
Obrigado @TarmoPikaro. Quando copiei do meu código de trabalho, perdi o DllImport. Também aumentou o tamanho do buffer de acordo com sua recomendação. Boa pegada!
Metalogic 05/04
abordagem impressionante
tbhaxor
Isso mostra apenas a unidade KB. A idéia é mostrar a maior unidade, dependendo do valor.
jstuardo
5

Mistura de todas as soluções :-)

    /// <summary>
    /// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
    /// kilobytes, megabytes, or gigabytes, depending on the size.
    /// </summary>
    /// <param name="fileSize">The numeric value to be converted.</param>
    /// <returns>The converted string.</returns>
    public static string FormatByteSize(double fileSize)
    {
        FileSizeUnit unit = FileSizeUnit.B;
        while (fileSize >= 1024 && unit < FileSizeUnit.YB)
        {
            fileSize = fileSize / 1024;
            unit++;
        }
        return string.Format("{0:0.##} {1}", fileSize, unit);
    }

    /// <summary>
    /// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
    /// kilobytes, megabytes, or gigabytes, depending on the size.
    /// </summary>
    /// <param name="fileInfo"></param>
    /// <returns>The converted string.</returns>
    public static string FormatByteSize(FileInfo fileInfo)
    {
        return FormatByteSize(fileInfo.Length);
    }
}

public enum FileSizeUnit : byte
{
    B,
    KB,
    MB,
    GB,
    TB,
    PB,
    EB,
    ZB,
    YB
}
NET3
fonte
4

Há um projeto de código aberto que pode fazer isso e muito mais.

7.Bits().ToString();         // 7 b
8.Bits().ToString();         // 1 B
(.5).Kilobytes().Humanize();   // 512 B
(1000).Kilobytes().ToString(); // 1000 KB
(1024).Kilobytes().Humanize(); // 1 MB
(.5).Gigabytes().Humanize();   // 512 MB
(1024).Gigabytes().ToString(); // 1 TB

http://humanizr.net/#bytesize

https://github.com/MehdiK/Humanizer

Jernej Novak
fonte
3

Como a solução da @ NET3. Use shift em vez de divisão para testar o intervalo bytes, porque a divisão leva mais custo da CPU.

private static readonly string[] UNITS = new string[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };

public static string FormatSize(ulong bytes)
{
    int c = 0;
    for (c = 0; c < UNITS.Length; c++)
    {
        ulong m = (ulong)1 << ((c + 1) * 10);
        if (bytes < m)
            break;
    }

    double n = bytes / (double)((ulong)1 << (c * 10));
    return string.Format("{0:0.##} {1}", n, UNITS[c]);
}
user1448446
fonte
2

Suponho que você esteja procurando "1,4 MB" em vez de "1468006 bytes"?

Eu não acho que exista uma maneira interna de fazer isso no .NET. Você precisará apenas descobrir qual unidade é apropriada e formatá-la.

Edit: Aqui está um código de exemplo para fazer exatamente isso:

http://www.codeproject.com/KB/cpp/formatsize.aspx

Peter Crabtree
fonte
2

Que tal alguma recursão:

private static string ReturnSize(double size, string sizeLabel)
{
  if (size > 1024)
  {
    if (sizeLabel.Length == 0)
      return ReturnSize(size / 1024, "KB");
    else if (sizeLabel == "KB")
      return ReturnSize(size / 1024, "MB");
    else if (sizeLabel == "MB")
      return ReturnSize(size / 1024, "GB");
    else if (sizeLabel == "GB")
      return ReturnSize(size / 1024, "TB");
    else
      return ReturnSize(size / 1024, "PB");
  }
  else
  {
    if (sizeLabel.Length > 0)
      return string.Concat(size.ToString("0.00"), sizeLabel);
    else
      return string.Concat(size.ToString("0.00"), "Bytes");
  }
}

Então você chama:

return ReturnSize(size, string.Empty);
RooiWillie
fonte
Bom, mas consome CPU
kamalpreet
1

Meus 2 centavos:

  • O prefixo para kilobyte é kB (K minúsculo)
  • Como essas funções são para fins de apresentação, deve-se fornecer uma cultura, por exemplo: string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
  • Dependendo do contexto, um kilobyte pode ter 1000 ou 1024 bytes . O mesmo vale para MB, GB, etc.
Berend
fonte
3
Um kilobyte significa 1000 bytes ( wolframalpha.com/input/?i=kilobyte ), não depende do contexto. É historicamente dependia de contexto, como wikipedia diz, e era de jure mudou em 1998 e de facto a mudança começou por volta de 2005, quando terabyte discos rígidos trouxe a atenção do público. O termo para 1024 bytes é kibibyte. O código que os alterna com base na cultura está produzindo informações incorretas.
Superbest
1

Mais uma abordagem, pelo que vale a pena. Gostei da solução otimizada do @humbads mencionada acima, por isso copiei o princípio, mas o implementei de maneira um pouco diferente.

Suponho que seja discutível se deve ser um método de extensão (já que nem todos os comprimentos são necessariamente tamanhos de bytes), mas eu gosto deles, e é em algum lugar que posso encontrar o método na próxima vez que precisar!

Em relação às unidades, acho que nunca disse 'Kibibyte' ou 'Mebibyte' em minha vida, e embora eu seja cético em relação a esses padrões impostos em vez de desenvolvidos, acho que evitará confusão a longo prazo. .

public static class LongExtensions
{
    private static readonly long[] numberOfBytesInUnit;
    private static readonly Func<long, string>[] bytesToUnitConverters;

    static LongExtensions()
    {
        numberOfBytesInUnit = new long[6]    
        {
            1L << 10,    // Bytes in a Kibibyte
            1L << 20,    // Bytes in a Mebibyte
            1L << 30,    // Bytes in a Gibibyte
            1L << 40,    // Bytes in a Tebibyte
            1L << 50,    // Bytes in a Pebibyte
            1L << 60     // Bytes in a Exbibyte
        };

        // Shift the long (integer) down to 1024 times its number of units, convert to a double (real number), 
        // then divide to get the final number of units (units will be in the range 1 to 1023.999)
        Func<long, int, string> FormatAsProportionOfUnit = (bytes, shift) => (((double)(bytes >> shift)) / 1024).ToString("0.###");

        bytesToUnitConverters = new Func<long,string>[7]
        {
            bytes => bytes.ToString() + " B",
            bytes => FormatAsProportionOfUnit(bytes, 0) + " KiB",
            bytes => FormatAsProportionOfUnit(bytes, 10) + " MiB",
            bytes => FormatAsProportionOfUnit(bytes, 20) + " GiB",
            bytes => FormatAsProportionOfUnit(bytes, 30) + " TiB",
            bytes => FormatAsProportionOfUnit(bytes, 40) + " PiB",
            bytes => FormatAsProportionOfUnit(bytes, 50) + " EiB",
        };
    }

    public static string ToReadableByteSizeString(this long bytes)
    {
        if (bytes < 0)
            return "-" + Math.Abs(bytes).ToReadableByteSizeString();

        int counter = 0;
        while (counter < numberOfBytesInUnit.Length)
        {
            if (bytes < numberOfBytesInUnit[counter])
                return bytesToUnitConverters[counter](bytes);
            counter++;
        }
        return bytesToUnitConverters[counter](bytes);
    }
}
Giles
fonte
0

Eu uso o método de extensão Long abaixo para converter em uma sequência de tamanho legível por humanos. Este método é a implementação em C # da solução Java desta mesma pergunta postada no Stack Overflow, aqui .

/// <summary>
/// Convert a byte count into a human readable size string.
/// </summary>
/// <param name="bytes">The byte count.</param>
/// <param name="si">Whether or not to use SI units.</param>
/// <returns>A human readable size string.</returns>
public static string ToHumanReadableByteCount(
    this long bytes
    , bool si
)
{
    var unit = si
        ? 1000
        : 1024;

    if (bytes < unit)
    {
        return $"{bytes} B";
    }

    var exp = (int) (Math.Log(bytes) / Math.Log(unit));

    return $"{bytes / Math.Pow(unit, exp):F2} " +
           $"{(si ? "kMGTPE" : "KMGTPE")[exp - 1] + (si ? string.Empty : "i")}B";
}
masterwok
fonte