Converting file size in bytes to human-readable string

Converting file size in bytes to human-readable string

I’m using this function to convert a file size in bytes to a human-readable file size:
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?

Solution 1:

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 2:

Here’s one I wrote:

``````function humanFileSize(bytes, si) {
var thresh = si ? 1000 : 1024;
if(Math.abs(bytes) < thresh) {
return bytes + ' B';
}
var units = si
? ['kB','MB','GB','TB','PB','EB','ZB','YB']
: ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
var u = -1;
do {
bytes /= thresh;
++u;
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
``````

e.g.

``````humanFileSize(5000,true)
> "5.0 kB"
humanFileSize(5000,false)
> "4.9 KiB"
humanFileSize(-10000000000000000000000000000)
> "-8271.8 YiB"
``````

Solution 3:

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 4:

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));
``````

Solution 5:

``````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( (3*1024*1024) );
//=> “3.00 MB”

Solution 6:

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,
};
``````