<?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')); |
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];
}
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];
}
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];
}
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];
}
// 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];
}
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";
}
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
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];
}
}
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]; }
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.
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);
how to revert it?
i want to convert human file-size to int bytes
@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;
}
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]; }
I modified @MrCaspan's method to address a few issues.
- It returns
0B
instead ofNANB
when attempting to format 0 bytes, which was caused by a value of-INF
calculated for the factor. - I added petabytes, as I think this is the most reasonable max unit to display in most cases. If someone wanted a lower or higher ceiling, the units can easily be modified.
- When the factor was higher than the highest defined units, the function would just return an integer with no unit indication. This modified version just uses the highest defined unit when the factor exceeds the defined bounds.
It's a longer version for sure, but it's more readable and I think addressing the issues above warrants a lengthier function.
function readableBytes(int $bytes): string
{
$unitDecimalsByFactor = [
['B', 0],
['kB', 0],
['MB', 2],
['GB', 2],
['TB', 3],
['PB', 3]
];
$factor = $bytes ? floor(log($bytes, 1024)) : 0;
$factor = min($factor, count($unitDecimalsByFactor) - 1);
$value = round($bytes / pow(1024, $factor), $unitDecimalsByFactor[$factor][1]);
$units = $unitDecimalsByFactor[$factor][0];
return $value.$units;
}
If this function were to be adapted for use with larger factors, a type other than int
would be required for the $bytes
parameter due to its maximum value.
Another version, eliminates needless count() units will always be 5, ** operator, optional unit output, reworked array for determining decimals by the factor
function human_bytes(int $bytes, $u = false): string {
if ($bytes < 1) return 0;
$units = ['B', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'];
$factor = min(floor(log($bytes, 1024)), 5);
$value = round($bytes / (1024 ** $factor), $factor > 1 ? 2 : 0);
return $u ? $value . $units[$factor] : $value;
}
Another version, eliminates needless count()
just FYI: count is "free" operation in php, cause array always have its size in internal structure.
This is ~40% faster.