human readable file size
Asked Answered
D

13

51
function humanFileSize($size)
{
    if ($size >= 1073741824) {
      $fileSize = round($size / 1024 / 1024 / 1024,1) . 'GB';
    } elseif ($size >= 1048576) {
        $fileSize = round($size / 1024 / 1024,1) . 'MB';
    } elseif($size >= 1024) {
        $fileSize = round($size / 1024,1) . 'KB';
    } else {
        $fileSize = $size . ' bytes';
    }
    return $fileSize;
}

... works great except: I can't manually choose in what format I need to display, say i want to show in MB only whatever the file size is. Currently if its in the GB range, it would only show in GB.

Also, how do I limit the decimal to 2?

Diatomite answered 3/3, 2013 at 16:51 Comment(0)
L
67

Try something like this:

function humanFileSize($size,$unit="") {
  if( (!$unit && $size >= 1<<30) || $unit == "GB")
    return number_format($size/(1<<30),2)."GB";
  if( (!$unit && $size >= 1<<20) || $unit == "MB")
    return number_format($size/(1<<20),2)."MB";
  if( (!$unit && $size >= 1<<10) || $unit == "KB")
    return number_format($size/(1<<10),2)."KB";
  return number_format($size)." bytes";
}
Luigi answered 3/3, 2013 at 16:56 Comment(9)
It should rather be $unit == "GB" || !$unit && $size >= 1<<30.Idoux
@Idoux Why? What difference does it make? Actually, that would break it for non-string parameters like 0.Luigi
Your solution requires the value to be at least 1 of the unit’s size, i.e. there is no way to get a value that is less than 1 like 0.5 GB. In such a case the result is displayed in bytes.Idoux
@Nimbuz This is probably because it's assuming 1MB == 1000000 Bytes (and it's actually 1024*1024 Bytes).Parquetry
I think this answer isn't very good. It's overly complicated from a semantic standpoint (why use 1<<10 instead of 1024?) and it doesn't cover units larger than GB.Record
@KrzysztofKarski Your feedback is welcome, but 1<<10 is used instead of 1024 for the same reason that 1<<30 is used instead of 1073741824 - it conveys more meaning. Furthermore, adding bigger units is trivial, simply add in the variant with 1<<40 and "TB", and beyond. The point of this answer isn't to be a 100% complete solution, hence the "Try something like this:" text.Luigi
Why not pow(1024, 3) then? It states your intent way more clearly than arriving at a desired number through bit shifting...Record
@KrzysztofKarski Because bit-shifting is the definition? It's also significantly more efficient.Luigi
@NiettheDarkAbsol that's premature optimization which is rendered moot by multiple logic branches.Record
S
61

There is great example by Jeffrey Sambells:

function human_filesize($bytes, $dec = 2): string {

    $size   = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
    $factor = floor((strlen($bytes) - 1) / 3);
    if ($factor == 0) $dec = 0;


    return sprintf("%.{$dec}f %s", $bytes / (1024 ** $factor), $size[$factor]);

}

echo human_filesize(filesize('example.zip'));
Sinner answered 27/5, 2014 at 11:58 Comment(8)
Nice, but I would add if($factor==0) $dec=0;Wipe
its not accurate, 4,281,016 bytes gives 4.08 MB while google says 4.28102 megabytesGent
i guess google is wrong tinking 1mb = 1000000 byes, it should b 1024*1024Gent
Google is not wrong. 1MB = 1000000B. 1MiB = 1048576B. They're just following the latest IEC standards for units: en.wikipedia.org/wiki/Binary_prefixRecord
For the return line you can also use: return sprintf("%.{$dec}f %s", $bytes / (1024 ** $factor), $size[$factor]); It's a bit cleaner.Leukorrhea
@Leukorrhea Thanks! I've been writing pow() for about 10 years now, and this is the first time I see the ** operator. It's incredible, since it's available since version 5.6 (: I also agree about the sprintf shortcut. Have You submitted it as an edit? If not, please do so.Damselfish
For big numbers the string representation on PHP is being expressed with scientific notation (1.05E+14 for example). That would break the function using strlen. I'd add something of this sort to remedy the issue: $bytes = number_format($bytes, 0, '.', '');Devastating
In the "return" line the exponentiation base should be 1000, not 1024, if the intended units are kB, MB, GB, etc. If 1024 is used, the abbreviations should be kiB, MiB, GiB, etc. Read about it in the "Kilobyte" article in Wikipedia.Carny
M
24

I'm using this method:

function byteConvert($bytes)
{
    if ($bytes == 0)
        return "0.00 B";

    $s = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
    $e = floor(log($bytes, 1024));

    return round($bytes/pow(1024, $e), 2).$s[$e];
}

works great in o(1).

Morganatic answered 20/1, 2015 at 14:35 Comment(0)
E
9

A pretty short 3 lines method that I use (1024 = 1KB) and supports from KB to YB is the following one:

<?php 

/**
 * Converts a long string of bytes into a readable format e.g KB, MB, GB, TB, YB
 * 
 * @param {Int} num The number of bytes.
 */
function readableBytes($bytes) {
    $i = floor(log($bytes) / log(1024));

    $sizes = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');

    return sprintf('%.02F', $bytes / pow(1024, $i)) * 1 . ' ' . $sizes[$i];
}

// "1000 B"
echo readableBytes(1000);

// "9.42 MB"
echo readableBytes(9874321);

// "9.31 GB"
// The number of bytes as a string is accepted as well
echo readableBytes("10000000000");

// "648.37 TB"
echo readableBytes(712893712304234);

// "5.52 PB"
echo readableBytes(6212893712323224);

More info about these methods on this article.

Exploration answered 31/3, 2018 at 13:48 Comment(1)
The * 1 code should be removed if you want to print the trailing zero, e.g. 6.40 MB.Canopy
R
6

To expand on Vaidas' answer, here's how you should do it to account for the new IEC standards:

function human_readable_bytes($bytes, $decimals = 2, $system = 'binary')
{
    $mod = ($system === 'binary') ? 1024 : 1000;

    $units = array(
        'binary' => array(
            'B',
            'KiB',
            'MiB',
            'GiB',
            'TiB',
            'PiB',
            'EiB',
            'ZiB',
            'YiB',
        ),
        'metric' => array(
            'B',
            'kB',
            'MB',
            'GB',
            'TB',
            'PB',
            'EB',
            'ZB',
            'YB',
        ),
    );

    $factor = floor((strlen($bytes) - 1) / 3);

    return sprintf("%.{$decimals}f%s", $bytes / pow($mod, $factor), $units[$system][$factor]);
}

Technically, according to the specifications for storage devices and such you should use the metric system as default (that's why Google converter shows kB -> MB as mod 1000 instead of 1024).

Record answered 29/7, 2016 at 12:42 Comment(2)
$size should be $bytesMacilroy
Fixed. Thanks for noticing.Record
S
6

Here's my custom function for displaying human readable file size:

function getHumanReadableSize($bytes) {
  if ($bytes > 0) {
    $base = floor(log($bytes) / log(1024));
    $units = array("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"); //units of measurement
    return number_format(($bytes / pow(1024, floor($base))), 3) . " $units[$base]";
  } else return "0 bytes";
}
Shortly answered 10/3, 2021 at 21:51 Comment(0)
B
3
function bytesToHuman($bytes)
{
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
    for ($i = 0; $bytes > 1024; $i++) $bytes /= 1024;
    return round($bytes, 2) . ' ' . $units[$i];
}

Credit: https://laracasts.com/discuss/channels/laravel/human-readable-file-size-and-time?page=1#reply=115796

Berliner answered 18/12, 2019 at 6:21 Comment(0)
H
1

You can modify your function to fullfil both your need to force a unit if given and adjust the precision.

function humanFileSize($size, $precision = 1, $show = "")
{
    $b = $size;
    $kb = round($size / 1024, $precision);
    $mb = round($kb / 1024, $precision);
    $gb = round($mb / 1024, $precision);

    if($kb == 0 || $show == "B") {
        return $b . " bytes";
    } else if($mb == 0 || $show == "KB") {
        return $kb . "KB";
    } else if($gb == 0 || $show == "MB") {
        return $mb . "MB";
    } else {
        return $gb . "GB";
    }
}

//Test with different values
echo humanFileSize(1038) . "<br />";    
echo humanFileSize(103053, 0) . "<br />";
echo humanFileSize(103053) . "<br />";
echo humanFileSize(1030544553) . "<br />";
echo humanFileSize(1030534053405, 2, "GB") . "<br />";  ;
Hallett answered 3/3, 2013 at 17:6 Comment(0)
P
1
function getHumanReadableSize($size, $unit = null, $decemals = 2) {
    $byteUnits = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    if (!is_null($unit) && !in_array($unit, $byteUnits)) {
        $unit = null;
    }
    $extent = 1;
    foreach ($byteUnits as $rank) {
        if ((is_null($unit) && ($size < $extent <<= 10)) || ($rank == $unit)) {
            break;
        }
    }
    return number_format($size / ($extent >> 10), $decemals) . $rank;
}

If php version below 5.4 use $byteUnits = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');

Phoenicia answered 19/11, 2013 at 8:15 Comment(0)
P
1

I wanted a function that returned filesizes like Windows does, and surprisingly I could find none at all. Even worse, some here and elsewhere are broken in that they assume 1KB = 1000B.

So I coded one! Plus two helper functions. Here they are:

// Returns a size in a human-readable form from a byte count.
function humanSize($bytes)
{
    if ($bytes < 1024) return "$bytes Bytes";

    $units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    foreach ($units as $i => $unit)
    {
        // The reason for this threshold is to avoid e.g., "1000 KB",
        // instead jumping from e.g., "999 KB" to "0.97 MB".
        $multiplier = pow(1024, $i + 1);
        $threshold = $multiplier * 1000;

        if ($bytes < $threshold)
        {
            $size = formatToMinimumDigits($bytes / $multiplier, false);
            return "$size $unit";
        }
    }
}

// Efficiently calculates how many digits the integer portion of a number has.
function digits($number)
{
    // Yes, I could convert to string and count the characters,
    // but this is faster and cooler.
    $log = log10($number);
    if ($log < 0) return 1;
    return floor($log) + 1;
}

// Formats a number to a minimum amount of digits.
// In other words, makes sure that a number has at least $digits on it, even if
// that means introducing redundant decimal zeroes at the end, or rounding the
// ones present exceeding the $digits count when combined with the integers.
// For example:
//     formatToMinimumDigits(10)           // 10.0
//     formatToMinimumDigits(1.1)          // 1.10
//     formatToMinimumDigits(12.34)        // 12.3
//     formatToMinimumDigits(1.234)        // 1.23
//     formatToMinimumDigits(1.203)        // 1.20
//     formatToMinimumDigits(123.4)        // 123
//     formatToMinimumDigits(100)          // 100
//     formatToMinimumDigits(1000)         // 1000
//     formatToMinimumDigits(1)            // 1.00
//     formatToMinimumDigits(1.002)        // 1.00
//     formatToMinimumDigits(1.005)        // 1.01
//     formatToMinimumDigits(1.005, false) // 1.00
// This is primarily useful for generating human-friendly numbers.
function formatToMinimumDigits($value, $round = true, $digits = 3)
{
    $integers = floor($value);

    $decimalsNeeded = $digits - digits($integers);

    if ($decimalsNeeded < 1)
    {
        return $integers;
    }
    else
    {
        if ($round)
        {
            // This relies on implicit type casting of float to string.
            $parts = explode('.', round($value, $decimalsNeeded));
            // We re-declare the integers because they may change
            // after we round the number.
            $integers = $parts[0];
        }
        else
        {
            // Again, implicit type cast to string.
            $parts = explode('.', $value);
        }

        // And because of the implicit type cast, we must guard against
        // 1.00 becoming 1, thus not exploding the second half of it.
        $decimals = isset($parts[1]) ? $parts[1] : '0';
        $joined = "$integers.$decimals".str_repeat('0', $digits);
        return substr($joined, 0, $digits + 1);
    }
}

Usage is as simple as humanSize(123456789).

Parquetry answered 18/12, 2013 at 7:25 Comment(0)
L
1

This is how I use, it's clean and simple. Also can be used like this:

public function getHumanReadableFilesize(int $bytes, int $dec = 2): string
{
    $size   = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    $factor = floor((strlen($bytes) - 1) / 3);

    return sprintf("%.{$dec}f %s", ($bytes / (1024 ** $factor)), $size[$factor]);
}

Thanks @Márton Tamás for suggestion to add like comment.

Leukorrhea answered 8/4, 2020 at 11:16 Comment(0)
B
0

I discovered a method with using switch without "breaks" for this task. I want to share with you. Also, I'm using a new "enum" feature that perfect fits here, I guess.

I made the code as an OOP class, so you can use it as a microservice, for example, in Symfony or Drupal.

enum Unit: string {
  case BYTES = 'bytes';
  case KB = 'Kb';
  case MB = 'Mb';
  case GB = 'Gb';
  case TB = 'Tb';
}

class HumanFileSize {
  private int $bytes;
  private ?Unit $unit;
  private int|float $result;

  public function __construct(int $bytes, ?Unit $unit = NULL) {
    $this->bytes = $bytes;
    $this->unit = $unit;
  }

  public function toUnit(Unit $unit): static {
    $this->unit = $unit;

    return $this;
  }

  public function __toString(): string {
    $this->calculate();

    return sprintf('%s %s<br>', is_int($this->result) ? $this->result : number_format($this->result, 2), $this->unit->value);
  }

  private function calculate(): void {
    $this->unit ? $this->calculateByUnit() : $this->calculateUnitAuto();
  }

  private function calculateByUnit(): void {
    $result = $this->bytes;

    switch ($this->unit) {
      case Unit::TB:
        $result /= 1024;
      case Unit::GB:
        $result /= 1024;
      case Unit::MB:
        $result /= 1024;
      case Unit::KB:
        $result /= 1024;
    }

    $this->result = $result;
  }

  private function calculateUnitAuto(): void {
    $result = $this->result = $this->bytes;

    $cases = Unit::cases();
    $factor = 0;

    while (($result /= 1024) >= 1) {
      $this->result = $result;
      $factor++;
    }

    $this->unit = $cases[$factor];
  }
}

And examples (client code):

// Auto units.
print new HumanFileSize(1023);  // 1023 bytes
print new HumanFileSize(1024);  // 1 Kb
print new HumanFileSize(1025);  // 1.00 Kb

// Convert same file size to your units.
$filesize = 123456;
$x = new HumanFileSize($filesize);
print $x;                       // 120.56 Kb (auto units)
// Set units.
print $x->toUnit(Unit::BYTES);  // 123456 bytes
$filesize_mb = $x->toUnit(Unit::MB);
print $filesize_mb;             // 0.12 Mb
Biquadratic answered 15/7, 2023 at 5:12 Comment(1)
The correct abbreviation for "kilo" is "k", lowercase, and the correct abbreviation for "byte" is "B", uppercase. Therefore, the abbreviation for "kilobyte" is "kB", not "KB" or "Kb". Also, megabyte, gigabyte and terabyte are abbreviated "MB", "GB" and "TB" per IEC standard practice. The division factor should be 1000, not 1024. As-is, this routine is calculating kibibytes, mibibytes, etc.Carny
K
-1

Here is a working function managing till Yottabyte:

function DisplayFileSize($size, $unit = false, $precision = 2){

$b = $size;
$kb = round($size / 1024, $precision);
$mb = round($kb / 1024, $precision);
$gb = round($mb / 1024, $precision);
$tb = round($gb / 1024, $precision);
$pb = round($tb / 1024, $precision);
$eb = round($pb / 1024, $precision);
$zb = round($eb / 1024, $precision);
$yb = round($zb / 1024, $precision);

if((!$unit && floor($kb) == 0) || $unit == "b") {
    return array("value" => FormatNumber($b), "unit" => "bytes");
} else if((!$unit && floor($mb) == 0) || $unit == "kb") {
    return array("value" => FormatNumber($kb, 2), "unit" => "Kb");
} else if((!$unit && floor($gb) == 0) || $unit == "mb") {
    return array("value" => FormatNumber($mb, 2), "unit" => "Mb");
 } else if((!$unit && floor($tb) == 0) || $unit == "gb") {
     return array("value" => FormatNumber($gb, 2), "unit" => "Gb");
 } else if((!$unit && floor($pb) == 0) || $unit == "tb") {
     return array("value" => FormatNumber($tb, 2), "unit" => "Tb");
 } else if((!$unit && floor($eb) == 0) || $unit == "pb") {
    return array("value" => FormatNumber($pb, 2), "unit" => "Pb");
 } else if((!$unit && floor($zb) == 0) || $unit == "eb") {
     return array("value" => FormatNumber($eb, 2), "unit" => "Eb");
 } else if((!$unit && floor($yb) == 0) || $unit == "zb") {
     return array("value" => FormatNumber($zb, 2), "unit" => "Zb");
} else {
    return array("value" => FormatNumber($yb, 2), "unit" => "Yb");
}

}

Kristinkristina answered 22/5, 2015 at 6:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.