Convert size in bytes to human readable format (JavaScript)

This is JavaScript implementation of the function described in my earlier article Convert size in bytes to a human readable format (PHP). Please see the link for more in-depth explanation of the function workflow.

  1. /**
  2.  * Convert number of bytes into human readable format
  3.  *
  4.  * @param integer bytes     Number of bytes to convert
  5.  * @param integer precision Number of digits after the decimal separator
  6.  * @return string
  7.  */
  8. function bytesToSize(bytes, precision)
  9. {  
  10.     var kilobyte = 1024;
  11.     var megabyte = kilobyte * 1024;
  12.     var gigabyte = megabyte * 1024;
  13.     var terabyte = gigabyte * 1024;
  14.    
  15.     if ((bytes >= 0) && (bytes < kilobyte)) {
  16.         return bytes + ' B';
  17.  
  18.     } else if ((bytes >= kilobyte) && (bytes < megabyte)) {
  19.         return (bytes / kilobyte).toFixed(precision) + ' KB';
  20.  
  21.     } else if ((bytes >= megabyte) && (bytes < gigabyte)) {
  22.         return (bytes / megabyte).toFixed(precision) + ' MB';
  23.  
  24.     } else if ((bytes >= gigabyte) && (bytes < terabyte)) {
  25.         return (bytes / gigabyte).toFixed(precision) + ' GB';
  26.  
  27.     } else if (bytes >= terabyte) {
  28.         return (bytes / terabyte).toFixed(precision) + ' TB';
  29.  
  30.     } else {
  31.         return bytes + ' B';
  32.     }
  33. }

Comments
1
It's not really optimistic, personally I use this, translated from a PHP function in the documentation :
function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
};
ThomasR, August 24th 2010, 14:01
2
@ThomasR, this is really great logic.
Anon, October 21st 2010, 16:58
3
There is a bug in ThomasR's code...

The last line should be " + sizes[i];"

function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
};
Ben Timby, January 29th 2011, 0:27
4
I see what happened now, the stupid comment box filters square brackets. Let me see if I can escape them...

--
There is a bug in ThomasR's code...

The last line should be " + sizes[[i]];"

function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[[i]];
};

--
Ben Timby, January 29th 2011, 0:28
5
BAH, oh well, there should be an i in between the square brackets so that the correct denomination is used, instead of all of them.
Ben Timby, January 29th 2011, 0:29
6
The issue was that you were using i inside the square brackets and that was supposed to be the "bbcode" for italic. As a temporary fix I removed support for that tag as no one's using it anyway. I need to make sure that I am not replacing bold, italic, etc. inside the code tags.

Sorry for inconvenience caused :)

Speaking about Ben's example. I do agree that it's a nice solution as well. Thanks for the input!
Andris, February 7th 2011, 11:42
7
If you want a decimal instead of rounding.
Replace Math.Round with .toFixed();

function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
};
Jonathan, June 5th 2011, 16:35
8
We could extend the units to cover more units.
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

We could also use SI binary prefixes instead of SI decimal prefixed.
    var sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
Jonathan, June 5th 2011, 16:44
9
The above function I wrote will print '8.0 bytes', you probably want that to be just '8 bytes'.

function bytesToSize(bytes) {
  var sizes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
  if (bytes == 0) return 'n/a';
  var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
  if (i == 0) { return (bytes / Math.pow(1024, i)) + ' ' + sizes[i]; }
  return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
}
Jonathan, June 5th 2011, 16:46
10
I like your solution, Jonathan. Looking back at the code I realize that it's not the shortest way of doing it but I guess we all see our flaws when we revisit the code we've written some time ago :)

But, nevertheless, thanks for your contribution! Much appreciated!
Andris, June 9th 2011, 20:53
11
Ok my turn... compressing the work of Jonathan a bit more we end up with the following:

function bytesToSize (bytes) {
  var sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
  if (bytes == 0) return 'n/a';
  var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
  return ((i == 0)? (bytes / Math.pow(1024, i)) : (bytes / Math.pow(1024, i)).toFixed(1)) + ' ' + sizes[i];
};
Fergus, June 15th 2011, 12:14
12
Here's a simplified version of the Jonathan's method.

function bytesToSize2(bytes) {
    var sizes = [ 'n/a', 'bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
    var i = +Math.floor(Math.log(bytes) / Math.log(1024));
    return  (bytes / Math.pow(1024, i)).toFixed( i ? 1 : 0 ) + ' ' + sizes[ isNaN( bytes ) ? 0 : i+1 ];
}


Here's my version of the same thing so.
http://bateru.com/news/2011/08/code-of-the-day-javascript-convert-bytes-to-kb-mb-gb-etc/
Larry Battle, November 28th 2011, 21:21
13
No offense guys but the floor/round/pow,... shizzle seems a lil over the top for such a function. It results in an "incorrect" space shown by all the rounds.
Furthermore parseInt... Really? :p There is no need for that. Just wrote this function in a minute and I would think it would give more decent result and the calculation time should be alot shorter.

function bytesToSize(bytes, precision) {
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    var posttxt = 0;
    if (bytes == 0) return 'n/a';
    while( bytes >= 1024 ) { 
        posttxt++;
        bytes = bytes / 1024;
    }   
    return bytes.toFixed(precision) + " " + sizes[posttxt];
}

Hopes this helps someone else!
me me me, January 1st 2012, 22:15
14
me me me, yes parseInt.  When I pass in a value < 1024, it's a string and .toFixed throws an error "bytes.toFixed is not a function".  Your function is fixed with either
return parseInt(bytes).toFixed...
or by adding at the top
bytes = bytes * 1;
Israel, March 8th 2012, 21:56
15
Lifesavers, all of you!

Implementing me me me and Israel's modifications I'm now using this with Backup Box.

/**
 * Convert number of bytes into human readable format
 *
 * @param integer bytes     Number of bytes to convert
 * @param integer precision Number of digits after the decimal separator
 * @return string
 */
function bytesToSize(bytes, precision) {
    var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    var posttxt = 0;
    if (bytes == 0) return 'n/a';
    while( bytes >= 1024 ) {
        posttxt++;
        bytes = bytes / 1024;
    }
    return parseInt(bytes).toFixed(precision) + " " + sizes[posttxt];
}
Eric Warnke, June 13th 2012, 7:40
16
Isreal, Eric, parseInt() converts bytes to an int making toFixed() irrelevent.  I think you had intended to use Number() here:

return Number(bytes).toFixed...
Chris Bricker, July 6th 2012, 17:28
17
Great thanks, but for 8 Bytes instead of 8.1 Bytes adding

...
        if (bytes == 0) return 'n/a';
        if (bytes < 1024) {
            return Number(bytes) + " " + sizes[posttxt];
        }
        while (bytes >= 1024) {
...


seems to work :)
Ben Hassad, July 18th 2012, 10:04
18
All these will fail on anything larger than 2^10*x where x is some positive integer (try 100). There's a better solution on StackOverflow: javascript - Actual numbers to the human readable values - Stack Overflow
Janus, September 28th 2012, 22:15
19
Looks like toFixed is getting confused with toPrecision.

function bytesToSize(bytes, precision) {
	var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
	var posttxt = 0;
	if (bytes == 0) return 'n/a';
	if (bytes < 1024) {
			return Number(bytes) + " " + sizes[posttxt];
	}
	while( bytes >= 1024 ) {
			posttxt++;
			bytes = bytes / 1024;
	}
	return bytes.toPrecision(precision) + " " + sizes[posttxt];
}
Jason, October 29th 2012, 19:46
20
<a href="Numeral.js">Numeral.js</a> has support for formatting file sizes as well as a number of other formats like currency, percentages, and times.
Adam, October 30th 2012, 17:29
21
Here is simple and powerful bytes formatter

/**
 * Convert content length to fixed string
 * @static
 * @param {Number} n - number of byte kibibyte or mibi..., cannot not be NaN, indefinite or negative
 * @param {Number} [f=0] - max fraction digits in range of 0..20
 * @param {Number} [e=0] - exponent based 1024, Math.pow(1024,e) stands for the unit of n
 * @return {String}
 * @throws {RangeError}
 */
function toSizeString(n,f,e){
	n=parseFloat(n);
	f>>>=0;
	e>>>=0;
	if(n!==n||1/n<0||1/n===-1/n||f<0||f>20||e<0){
		throw new RangeError("One of the three argument (number, fraction, exponent) is out of range");
	}
	var kibi=1024,mibi=1048576,gibi=1073741824,tebi=1099511627776;
	var v,u;
	n*=Math.pow(1024,e);
	if(n<kibi){
		v=n.toFixed(0);
		u="B";
	}else if(n<mibi){
		v=(n/kibi).toFixed(f);
		u="KiB";
	}else if(n<gibi){
		v=(n/mibi).toFixed(f);
		u="MiB";
	}else if(n<tebi){
		v=(n/gibi).toFixed(f);
		u="GiB";
	}else{
		v=(n/tebi).toFixed(f);
		u="TiB";
	}
	return v+" "+u;
}
//tests
[toSizeString(1000),"1000 B",
	toSizeString(1024                          ),"1 KiB",
	toSizeString(1024*123.456,                2),"123.46 KiB",
	toSizeString(123.456,                     2, 1),"123.46 KiB",
	toSizeString(1024*1024*123.456,           2),"123.46 MiB",
	toSizeString(123.456,                     2, 2),"123.46 MiB",
	toSizeString(1024*1024*1024*123.4567,     3),"123.457 GiB",
	toSizeString(123.4567,                    3, 3),"123.457 GiB",
	toSizeString(1024*1024*1024*1024*123.4567,3),"123.457 TiB"
].forEach(function(value,index,array){
	if(index&1){return;}
	var expected=array[index+1];
	console.assert(value===expected,"test failed at "+(index/2));
});
Fuwei Chin, May 14th 2014, 8:20
Name
Email (required)
will not be published
Website
Recaptcha
you will only be required to fill it in once in this session

You can use [code][/code] tags in your comments