Converting file size in bytes to human-readable string
JavascriptFilesizeHuman ReadableJavascript Problem Overview
I'm using this function to convert a file size in bytes to a human-readable file size:
function getReadableFileSizeString(fileSizeInBytes) {
var i = -1;
var byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
do {
fileSizeInBytes = fileSizeInBytes / 1024;
i++;
} while (fileSizeInBytes > 1024);
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
};
However, it seems like this isn't 100% accurate. For example:
getReadableFileSizeString(1551859712); // output is "1.4 GB"
Shouldn't this be "1.5 GB"
? It seems like the division by 1024 is losing precision. Am I totally misunderstanding something or is there a better way to do this?
Javascript Solutions
Solution 1 - Javascript
Here's one I wrote:
/**
* Format bytes as human-readable text.
*
* @param bytes Number of bytes.
* @param si True to use metric (SI) units, aka powers of 1000. False to use
* binary (IEC), aka powers of 1024.
* @param dp Number of decimal places to display.
*
* @return Formatted string.
*/
function humanFileSize(bytes, si=false, dp=1) {
const thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
const units = si
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let u = -1;
const r = 10**dp;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return bytes.toFixed(dp) + ' ' + units[u];
}
console.log(humanFileSize(1551859712)) // 1.4 GiB
console.log(humanFileSize(5000, true)) // 5.0 kB
console.log(humanFileSize(5000, false)) // 4.9 KiB
console.log(humanFileSize(-10000000000000000000000000000)) // -8271.8 YiB
console.log(humanFileSize(999949, true)) // 999.9 kB
console.log(humanFileSize(999950, true)) // 1.0 MB
console.log(humanFileSize(999950, true, 2)) // 999.95 kB
console.log(humanFileSize(999500, true, 0)) // 1 MB
Solution 2 - Javascript
Another embodiment of the calculation
function humanFileSize(size) {
var i = Math.floor( Math.log(size) / Math.log(1024) );
return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
};
Solution 3 - Javascript
It depends on whether you want to use the binary or decimal convention.
RAM, for instance, is always measured in binary, so to express 1551859712 as ~1.4GiB would be correct.
On the other hand, hard disk manufacturers like to use decimal, so they would call it ~1.6GB.
And just to be confusing, floppy disks use a mixture of the two systems - their 1MB is actually 1024000 bytes.
Solution 4 - Javascript
Here is a prototype to convert a number to a readable string respecting the new international standards.
> There are two ways to represent big numbers: You could either display > them in multiples of 1000 = 10 3 (base 10) or 1024 = 2 10 (base 2). If > you divide by 1000, you probably use the SI prefix names, if you > divide by 1024, you probably use the IEC prefix names. The problem > starts with dividing by 1024. Many applications use the SI prefix > names for it and some use the IEC prefix names. The current situation > is a mess. If you see SI prefix names you do not know whether the > number is divided by 1000 or 1024
https://wiki.ubuntu.com/UnitsPolicy
http://en.wikipedia.org/wiki/Template:Quantities_of_bytes
Object.defineProperty(Number.prototype,'fileSize',{value:function(a,b,c,d){
return (a=a?[1e3,'k','B']:[1024,'K','iB'],b=Math,c=b.log,
d=c(this)/c(a[0])|0,this/b.pow(a[0],d)).toFixed(2)
+' '+(d?(a[1]+'MGTPEZY')[--d]+a[2]:'Bytes');
},writable:false,enumerable:false});
This function contains no loop
, and so it's probably faster than some other functions.
Usage:
IEC prefix
console.log((186457865).fileSize()); // default IEC (power 1024)
//177.82 MiB
//KiB,MiB,GiB,TiB,PiB,EiB,ZiB,YiB
SI prefix
console.log((186457865).fileSize(1)); //1,true for SI (power 1000)
//186.46 MB
//kB,MB,GB,TB,PB,EB,ZB,YB
i set the IEC as default because i always used binary mode to calculate the size of a file... using the power of 1024
If you just want one of them in a short oneliner function:
SI
function fileSizeSI(a,b,c,d,e){
return (b=Math,c=b.log,d=1e3,e=c(a)/c(d)|0,a/b.pow(d,e)).toFixed(2)
+' '+(e?'kMGTPEZY'[--e]+'B':'Bytes')
}
//kB,MB,GB,TB,PB,EB,ZB,YB
IEC
function fileSizeIEC(a,b,c,d,e){
return (b=Math,c=b.log,d=1024,e=c(a)/c(d)|0,a/b.pow(d,e)).toFixed(2)
+' '+(e?'KMGTPEZY'[--e]+'iB':'Bytes')
}
//KiB,MiB,GiB,TiB,PiB,EiB,ZiB,YiB
Usage:
console.log(fileSizeIEC(7412834521));
if you have some questions about the functions just ask
Solution 5 - Javascript
sizeOf = function (bytes) {
if (bytes == 0) { return "0.00 B"; }
var e = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes/Math.pow(1024, e)).toFixed(2)+' '+' KMGTP'.charAt(e)+'B';
}
> sizeOf(2054110009);
> //=> "1.91 GB"
> sizeOf(7054110);
> //=> "6.73 MB"
> sizeOf( (310241024) );
> //=> "3.00 MB"
Solution 6 - Javascript
Solution as ReactJS Component
Bytes = React.createClass({
formatBytes() {
var i = Math.floor(Math.log(this.props.bytes) / Math.log(1024));
return !this.props.bytes && '0 Bytes' || (this.props.bytes / Math.pow(1024, i)).toFixed(2) + " " + ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][i]
},
render () {
return (
<span>{ this.formatBytes() }</span>
);
}
});
UPDATE For those using es6 here is a stateless version of this same component
const sufixes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const getBytes = (bytes) => {
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return !bytes && '0 Bytes' || (bytes / Math.pow(1024, i)).toFixed(2) + " " + sufixes[i];
};
const Bytes = ({ bytes }) => (<span>{ getBytes(bytes) }</span>);
Bytes.propTypes = {
bytes: React.PropTypes.number,
};
Solution 7 - Javascript
Based on cocco's idea, here's a less compact -but hopefully more comprehensive- example.
<!DOCTYPE html>
<html>
<head>
<title>File info</title>
<script>
<!--
function fileSize(bytes) {
var exp = Math.log(bytes) / Math.log(1024) | 0;
var result = (bytes / Math.pow(1024, exp)).toFixed(2);
return result + ' ' + (exp == 0 ? 'bytes': 'KMGTPEZY'[exp - 1] + 'B');
}
function info(input) {
input.nextElementSibling.textContent = fileSize(input.files[0].size);
}
-->
</script>
</head>
<body>
<label for="upload-file"> File: </label>
<input id="upload-file" type="file" onchange="info(this)">
<div></div>
</body>
</html>
Solution 8 - Javascript
Another example similar to those here
function fileSize(b) {
var u = 0, s=1024;
while (b >= s || -b >= s) {
b /= s;
u++;
}
return (u ? b.toFixed(1) + ' ' : b) + ' KMGTPEZY'[u] + 'B';
}
It measures negligibly better performance than the others with similar features.
Solution 9 - Javascript
I wanted the "file manager" behavior (e.g., Windows Explorer) where the number of decimal places is proportional to the number size. Seemingly none of the other answers does this.
function humanFileSize(size) {
if (size < 1024) return size + ' B'
let i = Math.floor(Math.log(size) / Math.log(1024))
let num = (size / Math.pow(1024, i))
let round = Math.round(num)
num = round < 10 ? num.toFixed(2) : round < 100 ? num.toFixed(1) : round
return `${num} ${'KMGTPEZY'[i-1]}B`
}
Here's some examples:
humanFileSize(0) // "0 B"
humanFileSize(1023) // "1023 B"
humanFileSize(1024) // "1.00 KB"
humanFileSize(10240) // "10.0 KB"
humanFileSize(102400) // "100 KB"
humanFileSize(1024000) // "1000 KB"
humanFileSize(12345678) // "11.8 MB"
humanFileSize(1234567890) // "1.15 GB"
Solution 10 - Javascript
As of 2020, you can use file-size npm package, that supports formatting in IEC (power 1024, default), SI (power 1000), and JEDEC (Alternative SI Unit Notation).
npm install file-size
import filesize from "filesize";
// outputs: 186.46 MB
filesize(186457865).human('si');
// outputs: 177.82 MiB
filesize(186457865).human();
Solution 11 - Javascript
My answer might be late, but I guess it will help someone.
Metric prefix:
/**
* Format file size in metric prefix
* @param fileSize
* @returns {string}
*/
const formatFileSizeMetric = (fileSize) => {
let size = Math.abs(fileSize);
if (Number.isNaN(size)) {
return 'Invalid file size';
}
if (size === 0) {
return '0 bytes';
}
const units = ['bytes', 'kB', 'MB', 'GB', 'TB'];
let quotient = Math.floor(Math.log10(size) / 3);
quotient = quotient < units.length ? quotient : units.length - 1;
size /= (1000 ** quotient);
return `${+size.toFixed(2)} ${units[quotient]}`;
};
Binary prefix:
/**
* Format file size in binary prefix
* @param fileSize
* @returns {string}
*/
const formatFileSizeBinary = (fileSize) => {
let size = Math.abs(fileSize);
if (Number.isNaN(size)) {
return 'Invalid file size';
}
if (size === 0) {
return '0 bytes';
}
const units = ['bytes', 'kiB', 'MiB', 'GiB', 'TiB'];
let quotient = Math.floor(Math.log2(size) / 10);
quotient = quotient < units.length ? quotient : units.length - 1;
size /= (1024 ** quotient);
return `${+size.toFixed(2)} ${units[quotient]}`;
};
Examples:
// Metrics prefix
formatFileSizeMetric(0) // 0 bytes
formatFileSizeMetric(-1) // 1 bytes
formatFileSizeMetric(100) // 100 bytes
formatFileSizeMetric(1000) // 1 kB
formatFileSizeMetric(10**5) // 10 kB
formatFileSizeMetric(10**6) // 1 MB
formatFileSizeMetric(10**9) // 1GB
formatFileSizeMetric(10**12) // 1 TB
formatFileSizeMetric(10**15) // 1000 TB
// Binary prefix
formatFileSizeBinary(0) // 0 bytes
formatFileSizeBinary(-1) // 1 bytes
formatFileSizeBinary(1024) // 1 kiB
formatFileSizeBinary(2048) // 2 kiB
formatFileSizeBinary(2**20) // 1 MiB
formatFileSizeBinary(2**30) // 1 GiB
formatFileSizeBinary(2**40) // 1 TiB
formatFileSizeBinary(2**50) // 1024 TiB
Solution 12 - Javascript
There are lots of great answers here. But if your looking for a really simple way, and you don't mind a popular library, a great solution is filesize
https://www.npmjs.com/package/filesize
It has lots of options and the usage is simple e.g.
filesize(265318); // "259.1 KB"
Taken from their excellent examples
Solution 13 - Javascript
Here's mine - works for really big files too -_-
function formatFileSize(size)
{
var sizes = [' Bytes', ' KB', ' MB', ' GB', ' TB', ' PB', ' EB', ' ZB', ' YB'];
for (var i = 1; i < sizes.length; i++)
{
if (size < Math.pow(1024, i)) return (Math.round((size/Math.pow(1024, i-1))*100)/100) + sizes[i-1];
}
return size;
}
Solution 14 - Javascript
Based on cocco's answer but slightly desugerified (honestly, ones I was comfortable with are remained/added) and doesn't show trailing zeros but still supports 0, hope to be useful for others:
function fileSizeSI(size) {
var e = (Math.log(size) / Math.log(1e3)) | 0;
return +(size / Math.pow(1e3, e)).toFixed(2) + ' ' + ('kMGTPEZY'[e - 1] || '') + 'B';
}
// test:
document.write([0, 23, 4322, 324232132, 22e9, 64.22e12, 76.22e15, 64.66e18, 77.11e21, 22e24].map(fileSizeSI).join('<br>'));
Solution 15 - Javascript
1551859712 / 1024 = 1515488
1515488 / 1024 = 1479.96875
1479.96875 / 1024 = 1.44528198242188
Your solution is correct. The important thing to realize is that in order to get from 1551859712
to 1.5
, you have to do divisions by 1000, but bytes are counted in binary-to-decimal chunks of 1024, hence why the Gigabyte value is less.
Solution 16 - Javascript
A simple and short "Pretty Bytes" function for the SI system without the unnecessary fractionals rounding.
In fact, because the number size is supposed to be human-readable, a "1 of a thousand fraction" display is no longer human.
The number of decimal places is defaulted to 2 but can be modified on calling the function to other values. The common mostly display is the default 2 decimal place.
The code is short and uses the method of Number String Triplets.
// Simple Pretty Bytes with SI system
// Without fraction rounding
function numberPrettyBytesSI(Num=0, dec=2){
if (Num<1000) return Num+" Bytes";
Num =("0".repeat((Num+="").length*2%3)+Num).match(/.{3}/g);
return Number(Num[0])+"."+Num[1].substring(0,dec)+" "+" kMGTPEZY"[Num.length]+"B";
}
console.log(numberPrettyBytesSI(0));
console.log(numberPrettyBytesSI(500));
console.log(numberPrettyBytesSI(1000));
console.log(numberPrettyBytesSI(15000));
console.log(numberPrettyBytesSI(12345));
console.log(numberPrettyBytesSI(123456));
console.log(numberPrettyBytesSI(1234567));
console.log(numberPrettyBytesSI(12345678));
Solution 17 - Javascript
I found @cocco's answer interesting, but had the following issues with it:
- Don't modify native types or types you don't own
- Write clean, readable code for humans, let minifiers optimize code for machines
- (Bonus for TypeScript users) Doesn't play well with TypeScript
TypeScript:
/**
* Describes manner by which a quantity of bytes will be formatted.
*/
enum ByteFormat {
/**
* Use Base 10 (1 kB = 1000 bytes). Recommended for sizes of files on disk, disk sizes, bandwidth.
*/
SI = 0,
/**
* Use Base 2 (1 KiB = 1024 bytes). Recommended for RAM size, size of files on disk.
*/
IEC = 1
}
/**
* Returns a human-readable representation of a quantity of bytes in the most reasonable unit of magnitude.
* @example
* formatBytes(0) // returns "0 bytes"
* formatBytes(1) // returns "1 byte"
* formatBytes(1024, ByteFormat.IEC) // returns "1 KiB"
* formatBytes(1024, ByteFormat.SI) // returns "1.02 kB"
* @param size The size in bytes.
* @param format Format using SI (Base 10) or IEC (Base 2). Defaults to SI.
* @returns A string describing the bytes in the most reasonable unit of magnitude.
*/
function formatBytes(
value: number,
format: ByteFormat = ByteFormat.SI
) {
const [multiple, k, suffix] = (format === ByteFormat.SI
? [1000, 'k', 'B']
: [1024, 'K', 'iB']) as [number, string, string]
// tslint:disable-next-line: no-bitwise
const exp = (Math.log(value) / Math.log(multiple)) | 0
// or, if you'd prefer not to use bitwise expressions or disabling tslint rules, remove the line above and use the following:
// const exp = value === 0 ? 0 : Math.floor(Math.log(value) / Math.log(multiple))
const size = Number((value / Math.pow(multiple, exp)).toFixed(2))
return (
size +
' ' +
(exp
? (k + 'MGTPEZY')[exp - 1] + suffix
: 'byte' + (size !== 1 ? 's' : ''))
)
}
// example
[0, 1, 1024, Math.pow(1024, 2), Math.floor(Math.pow(1024, 2) * 2.34), Math.pow(1024, 3), Math.floor(Math.pow(1024, 3) * 892.2)].forEach(size => {
console.log('Bytes: ' + size)
console.log('SI size: ' + formatBytes(size))
console.log('IEC size: ' + formatBytes(size, 1) + '\n')
});
Solution 18 - Javascript
This is size improvement of mpen answer
function humanFileSize(bytes, si=false) {
let u, b=bytes, t= si ? 1000 : 1024;
['', si?'k':'K', ...'MGTPEZY'].find(x=> (u=x, b/=t, b**2<1));
return `${u ? (t*b).toFixed(1) : bytes} ${u}${!si && u ? 'i':''}B`;
}
function humanFileSize(bytes, si=false) {
let u, b=bytes, t= si ? 1000 : 1024;
['', si?'k':'K', ...'MGTPEZY'].find(x=> (u=x, b/=t, b**2<1));
return `${u ? (t*b).toFixed(1) : bytes} ${u}${!si && u ? 'i':''}B`;
}
// TEST
console.log(humanFileSize(5000)); // 4.9 KiB
console.log(humanFileSize(5000,true)); // 5.0 kB
Solution 19 - Javascript
For those who use Angular
, there's a package called angular-pipes
that has a pipe for this:
File
import { BytesPipe } from 'angular-pipes';
Usage
{{ 150 | bytes }} <!-- 150 B -->
{{ 1024 | bytes }} <!-- 1 KB -->
{{ 1048576 | bytes }} <!-- 1 MB -->
{{ 1024 | bytes: 0 : 'KB' }} <!-- 1 MB -->
{{ 1073741824 | bytes }} <!-- 1 GB -->
{{ 1099511627776 | bytes }} <!-- 1 TB -->
{{ 1073741824 | bytes : 0 : 'B' : 'MB' }} <!-- 1024 MB -->
Solution 20 - Javascript
To have the number of decimals dynamically adjusted in the voted solution, convert the bytes.toFixed(dp)
to number and then back to string like this:
return Number(bytes.toFixed(dp)).toString() + " " + units[u];
This will show 100 GiB instead of 100.00 GiB. Reference to question https://stackoverflow.com/questions/62147160/tofixed-dynamically-in-js?noredirect=1&lq=1
Solution 21 - Javascript
The typescript version to @Andrew V answer with the new "Template Literal Types"
export const humanFileSize = (bytes: number): `${number} ${'B' | 'KB' | 'MB' | 'GB' | 'TB'}` => {
const index = Math.floor(Math.log(bytes) / Math.log(1024));
return `${Number((bytes / Math.pow(1024, index)).toFixed(2)) * 1} ${(['B', 'KB', 'MB', 'GB', 'TB'] as const)[index]}`;
};
Solution 22 - Javascript
let bytes = 1024 * 10 * 10 * 10;
console.log(getReadableFileSizeString(bytes))
will return 1000.0Кб instead of 1MB