Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Human Readable File Size with PHP
<?php
# http://jeffreysambells.com/2012/10/25/human-readable-filesize-php
function human_filesize($bytes, $decimals = 2) {
$size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor];
}
echo human_filesize(filesize('example.zip'));
@redecs
Copy link

redecs commented Jun 18, 2015

This is ~40% faster.

function human_filesize($size, $precision = 2) {
    $units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
    $step = 1024;
    $i = 0;
    while (($size / $step) > 0.9) {
        $size = $size / $step;
        $i++;
    }
    return round($size, $precision).$units[$i];
}

@jameschens
Copy link

jameschens commented Sep 10, 2015

Same function as redecs with some minor golfing

function human_filesize($size, $precision = 2) {
    for($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {}
    return round($size, $precision).['B','kB','MB','GB','TB','PB','EB','ZB','YB'][$i];
}

@kfriend
Copy link

kfriend commented Jun 18, 2016

static should save a tiny bit more time.

function human_filesize($size, $precision = 2) {
    static $units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
    $step = 1024;
    $i = 0;
    while (($size / $step) > 0.9) {
        $size = $size / $step;
        $i++;
    }
    return round($size, $precision).$units[$i];
}

@MrCaspan
Copy link

MrCaspan commented Jan 13, 2017

Same as above but precision is chosen based on the result. No on likes seeing 123.34 kB precision maters on larger files but on smaller files its not as important. This accounts for this issue

function human_filesize($size) {
for($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {}
return round($size, [0,0,1,2,2,3,3,4,4][$i]).['B','kB','MB','GB','TB','PB','EB','ZB','YB'][$i];
}

@MrCaspan
Copy link

MrCaspan commented Jan 13, 2017

Even better

function MakeReadable($bytes) {
    $i = floor(log($bytes, 1024));
    return round($bytes / pow(1024, $i), [0,0,2,2,3][$i]).['B','kB','MB','GB','TB'][$i];
}

@gladx
Copy link

gladx commented Aug 23, 2017

// https://gist.github.com/gladx/62fa307eb65586b6dbaaad75273c653d

function human_filesize($bytes, $decimals = 2)
{
    if ($bytes < 1024) {
        return $bytes . ' B';
    }

    $factor = floor(log($bytes, 1024));
    return sprintf("%.{$decimals}f ", $bytes / pow(1024, $factor)) . ['B', 'KB', 'MB', 'GB', 'TB', 'PB'][$factor];
}

@AjmalPraveeN
Copy link

AjmalPraveeN commented Jul 20, 2018

The below code is not my code, (I want to know which is better, Which is faster and reliable) ? can you please someone suggest me a Good code?

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

@kdion4891
Copy link

kdion4891 commented Dec 18, 2019

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

@TyrotoxismB
Copy link

TyrotoxismB commented May 6, 2020

I took what I consider to be the best ideas in this thread and made what I consider the most appealing implementation of this problem. Feel free to use as you wish.

class Files
{
    const BYTE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
    const BYTE_PRECISION = [0, 0, 1, 2, 2, 3, 3, 4, 4];
    const BYTE_NEXT = 1024;

    /**
     * Convert bytes to be human readable.
     *
     * @param int      $bytes     Bytes to make readable
     * @param int|null $precision Precision of rounding
     *
     * @return string Human readable bytes
     */
    public static function HumanReadableBytes ($bytes, $precision = null)
    {
        for ($i = 0; ($bytes / self::BYTE_NEXT) >= 0.9 && $i < count(self::BYTE_UNITS); $i++) $bytes /= self::BYTE_NEXT;
        return round($bytes, is_null($precision) ? self::BYTE_PRECISION[$i] : $precision) . self::BYTE_UNITS[$i];
    }
}

@VijayS1
Copy link

VijayS1 commented Nov 25, 2020

I took what I consider to be the best ideas in this thread and made what I consider the most appealing implementation of this problem. Feel free to use as you wish.

class Files
{
    const BYTE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
    const BYTE_PRECISION = [0, 0, 1, 2, 2, 3, 3, 4, 4];
    const BYTE_NEXT = 1024;

    /**
     * Convert bytes to be human readable.
     *
     * @param int      $bytes     Bytes to make readable
     * @param int|null $precision Precision of rounding
     *
     * @return string Human readable bytes
     */
    public static function HumanReadableBytes ($bytes, $precision = null)
    {
        for ($i = 0; ($bytes / self::BYTE_NEXT) >= 0.9 && $i < count(self::BYTE_UNITS); $i++) $bytes /= self::BYTE_NEXT;
        return round($bytes, is_null($precision) ? self::BYTE_PRECISION[$i] : $precision) . self::BYTE_UNITS[$i];
    }
}

You have to add this line for typecasting $bytes or else you will get tons of PHP NOTICEs about malformed numbers
$bytes = (int) $bytes; //typecast to int to suppress PHP NOTICE

Anyway I edited it to use the MiB suffixes.

  const BYTE_UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
  const BYTE_PRECISION = [0, 0, 1, 2, 2, 3, 3, 4, 4];
  const BYTE_NEXT = 1024;
  public function fileSizeInfo($bytes, $precision = null) {
  	$bytes = (int) $bytes; //typecast to int to suppress PHP NOTICE
  	for ($i = 0; ($bytes / self::BYTE_NEXT) >= 0.9 && $i < count(self::BYTE_UNITS); $i++) $bytes /= self::BYTE_NEXT;
  	return round($bytes, is_null($precision) ? self::BYTE_PRECISION[$i] : (int)$precision) . self::BYTE_UNITS[$i];
  }

@ve3
Copy link

ve3 commented Jan 4, 2021

Even better

function MakeReadable($bytes) {
    $i = floor(log($bytes, 1024));
    return round($bytes / pow(1024, $i), [0,0,2,2,3][$i]).['B','kB','MB','GB','TB'][$i];
}

Can cause error Division by zero. if bytes enter is 0.

@crabmusket
Copy link

crabmusket commented Apr 12, 2021

Enterprise-grade OOP version 😁 (requires PHP 7.4)

  • comes with options built-in
  • currently uses binary prefixes instead of colloquial ones
class BytesForHumans
{
    public const FAMILIAR_UNIT_SCALE = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    public const PEDANTIC_UNIT_SCALE = ['B', 'kiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];

    public string $formatString = "%g%s";
    public int $logBase = 1024;
    public int $maxDecimalPlaces = 2;
    public array $unitScale = self::PEDANTIC_UNIT_SCALE;

    /** Create a BytesForHumans from a number of bytes. Fractional bytes are not allowed. */
    public static function fromBytes(int $bytes)
    {
        if ($bytes < 0) {
            throw new \DomainException("cannot have negative bytes");
        }

        return new static($bytes);
    }

    /** Display for humans by converting to string. */
    public function __toString()
    {
        [$number, $power] = $this->scaledValueAndPower();
        $units = $this->getUnits($power);
        return sprintf($this->formatString, round($number, $this->maxDecimalPlaces), $units);
    }

    /** You can also get the "raw" scaled value and its log-base-1024 power. */
    public function scaledValueAndPower(): array
    {
        if ($this->bytes == 0) {
            return [0, 0];
        }

        $power = floor(log($this->bytes, $this->logBase));
        $value = $this->bytes / pow($this->logBase, $power);
        return [$value, $power];
    }

    /** For fluent setting of public properties. */
    public function tap(\Closure $callback): self
    {
        $callback($this);
        return $this;
    }

    protected int $bytes;

    protected function __construct(int $bytes)
    {
        $this->bytes = $bytes;
    }

    protected function getUnits($power): string
    {
        if ($power >= count($this->unitScale)) {
            throw new \DomainException("cannot format bytes, too many bytes!");
        }

        return $this->unitScale[$power];
    }
}

Full usage example:

$binaryPrefixBytes = BytesForHumans::fromBytes(5945766364)
    ->tap(function($b) {
        $b->formatString = BytesForHumans::SPACED_FORMAT;
        $b->maxDecimalPlaces = 1;
    });

PHPUnit\Framework\Assert::assertEquals('5.5 GiB', (string)$binaryPrefixBytes);

$decimalPrefixBytes = BytesForHumans::fromBytes(5945766364)
    ->tap(function($b) {
        $b->unitScale = BytesForHumans::FAMILIAR_UNIT_SCALE;
        $b->logBase = 1000;
    });

PHPUnit\Framework\Assert::assertEquals('5.95GB', (string)$decimalPrefixBytes);

@Be1zebub
Copy link

Be1zebub commented Jun 1, 2022

how to revert it?
i want to convert human file-size to int bytes

@buldezir
Copy link

buldezir commented Jun 12, 2022

@Be1zebub that is for wget progress format, but the concept is same

function transferedToBytes(string $transfered): int
{
    $mods = ['B' => 1, 'K' => 1024, 'M' => 1024 ** 2, 'G' => 1024 ** 3];
    $transfered = str_replace(',', '.', $transfered);
    $mod = $transfered[strlen($transfered) - 1];
    return isset($mods[$mod]) ? (int)round(((float)$transfered) * $mods[$mod]) : 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment