Converting file size in bytes to human-readable string
Asked Answered
G

25

366

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 /= 1024;
    i++;
  } while (fileSizeInBytes > 1024);

  return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
}

console.log(getReadableFileSizeString(1551859712)); // output is "1.4 GB"

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?

Gaughan answered 2/5, 2012 at 19:27 Comment(8)
@Brendan... thanks! I appreciate that :) I'll be honest though... I didn't come up with this all by myself. I'm pretty sure I saw something similar somewhere at some point.Gaughan
your function will fail on anything larger than about 2^90.Roque
@JanusTroelsen... why is that? please give me some more details!Gaughan
getReadableFileSizeString(0); returns 0.1kb ;pTelugu
Why should it be 1.5? It's 1.445281982421875 which correctly rounds down to 1.4.Shandrashandrydan
1551859712/(1024^3)=1.445281982421875 which is correct!Sibelius
I love that you added YB. Doubtful anyone will get even 1 YB for his DB. It will cost 100 trillion dollars!Albin
@guyarad - there is a famous picture of a 5MB hard drive from 50 years ago (was at the size of a room and weighed about a ton). i'm sure back then they didn't even dream about GB and TB, and look at where we are today... never say never ;-)Dambrosio
P
66

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.

Pillsbury answered 2/5, 2012 at 19:31 Comment(2)
supper funny ;-) "just to be confusing, floppy disks use a mixture of the two systems - their 1MB is actually 1024000 bytes."Petulancy
true, RAM sizes are measured using IEC units, disk sizes using metric.. there's an isomorphic npm module to convert both: byte-sizeAlmucantar
S
570

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
Shandrashandrydan answered 17/2, 2013 at 8:57 Comment(16)
I'm making one adjustment: When evaluating the threshold, take the absolute value. This way the function will support negative values. Nice function! Thank you for not using a switch statement!!Voigt
@AaronBlenkush: When would you have a negative file size?Shandrashandrydan
I just copied your function into a Google Sheet I'm using to show size delta after a "cleanup" operation. Before, After, and Diff. The cleanup operation resulted in the growth of some database tables, and the reduction in others. For example, Table A has a diff of -1.95 MB, while Table B has a diff of 500 kB. Therefore: positive and negative :-)Voigt
Here is the compressed version of the script: function humanFileSize(B,i){var e=i?1e3:1024;if(Math.abs(B)<e)return B+" B";var a=i?["kB","MB","GB","TB","PB","EB","ZB","YB"]:["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],t=-1;do B/=e,++t;while(Math.abs(B)>=e&&t<a.length-1);return B.toFixed(1)+" "+a[t]}Szeged
@RAnders00: Thanks for the minified version. Can you tell me, though, why you inserted the two invisible Unicode characters U+200C (ZERO WIDTH NON-JOINER) and U+200B (ZERO WIDTH SPACE) after the E oft EiB? Is this intended to be a watermark, so that you can track who used this code? If so, I think you should have made that transparent in your post.Captainship
@Captainship I didn't insert them on purpose. I just ran it through UglifyJS and pasted it here, so I'm not sure why these characters are even there...Szeged
@Shandrashandrydan when you calculate a disk space delta for example.Abbess
@Frosty Z: The correct SI prefix is lowercase k. Source: mathworld.wolfram.com/SIPrefixes.htmlShandrashandrydan
@Shandrashandrydan sorry you're right. According to Wikipedia article on "kilobyte", it's still uppercase K for KiB. Counter intuitive...Slobbery
humanFileSize(999990,true); returns 1000.0 kB instead of 1.0 MB. So I added bytes = parseFloat(bytes.toFixed(1)); after ++u;Ard
@Ard You're rounding on every iteration which will create an increasing error margin. I've updated the answer with an alternative fix. Thanks for pointing that out!Shandrashandrydan
@Shandrashandrydan I like what you did better, but as a side point, what's the problem with having a small error margin if you're going to divide it by 1000 or 1024 anyway?Ard
@Ard Probably not a big deal at all.Shandrashandrydan
I see that you updated to code to add dp. Could you please explain what is it?Boloney
@Boloney Decimal places. You can see some examples with different numbers at the bottom. I'll update the code with a doc-comment.Shandrashandrydan
Cool variables names, bro... 😄Desperation
D
178

Another embodiment of the calculation

function humanFileSize(size) {
    var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
    return +((size / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
Dolhenty answered 22/12, 2013 at 17:32 Comment(7)
seems doesn't handle 0Rhodonite
It does or does not handle 0? After all, this with a if(size == 0) {} else {} is still more elegant than most I've seen.Krug
Changing the first line to var i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) ); seems to do the trick if it's 0. It will return "0 B".Tierratiersten
Just FYI; I know the answer is plain JavaScript, but if someone wan't to use it in TypeScript, it doesn't work (not typed correctly, as you're doing toFixed and then doing math with a string. What does the * 1 do?Aeriela
The *1 changes the data type from string to number, so for the value 1024 you get 1 kB instead of 1.00 kB. You can make TypeScript happy by doing Number((size / Math.pow(1024, i)).toFixed(2)) to accomplish the same thing.Jilt
Doesn’t handle sizes larger than TB, returning the number and undefined.Amaryl
To handle decimal sizes and negative numbers: var i = size < 1 ? 0 : ... and var sign = size < 0 ? '-' : ''; size = Math.abs(size); return sign + ...Decency
P
66

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.

Pillsbury answered 2/5, 2012 at 19:31 Comment(2)
supper funny ;-) "just to be confusing, floppy disks use a mixture of the two systems - their 1MB is actually 1024000 bytes."Petulancy
true, RAM sizes are measured using IEC units, disk sizes using metric.. there's an isomorphic npm module to convert both: byte-sizeAlmucantar
P
40

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

Populace answered 9/12, 2013 at 4:11 Comment(8)
very nice compact code, I'd personally add a couple of extra chars for control of the decimal places though.Loony
Hi! Actually the code is how i wrote it the first time in jsfiddle. In the last years i learned myself to use shorthand and bitwise. Slow mobile devices, slow internet, not much space... doing so i saved much time. But thats not all, the overall perfromance increased in every browser drastically and the whole code loads much faster ... i don't use jquery so i don't have to load 100kb everytime. I also need to say that i write javascript also in microcontrollers, Smart TV's, game consoles. those have limited space(MCU's), performance(SmartTV's) and naturally sometimes slow connnection(Mobile)Populace
Said that i hope you understand my choice. All i can do is explain what you don't understand or at the other side i'm always happy to learn new things. If there is something in my code that could increase the performance or save space i'm happy to hear it.Populace
Another point is that on many questions there is already a nice readable jquery solution. Sometimes you find no native javascript... in that case i always like to post the "vanilla" solution. Using pure old js. which most of the time is not much longer than the js way. Btw in this specific answer i just wanted to avoid LOOPS.... i'm not even shure if it's faster than the other functions. Theoretically it's faster ... but it uses pow & log which could slow down everything.Populace
I should also mention that i always cache things like 'window.document' or 'math.something' in a global var as i need them many times in various functions. Doing so i increase, most of all, the performance. Useless to say that also space is saved. "d=1e3,e=log(a)/log(d)|0,a/pow(d,e)" .... b & c not needed anymore.Populace
Minification should be part of your build process, not your coding style. No serious developer will use this code because of that as it takes too long to read and verify correctness.Ulcer
For those who hate to see "15.00 Bytes", you can just modified this part a bit: .toFixed(e ? 2 : 0)Ventail
Can you explain these parameters a, b, c, d, e?Downwards
N
25
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"

Neubauer answered 23/1, 2015 at 23:47 Comment(1)
If you wanted to get rid of the extra space for bytes, you could use the zero width space \u200b: '\u200bKMGTP'.Mauramauralia
E
17

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,
};
Elielia answered 10/12, 2015 at 19:27 Comment(2)
Great, thanks. You just forgot "bytes" inside Math.log() in the first line of getBytes functionPathy
Very nice. For disambiguation, and with ES6 notation, you might use this: return (!bytes && '0 Bytes') || ${(bytes / (1024 ** i)).toFixed(2)} ${suffixes[i]};Ab
H
13

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.

Haphtarah answered 30/12, 2016 at 20:46 Comment(1)
This does provide better performance than some other answers. I'm using this. Some others made my Chrome tabs hang and take 99.9% CPU as I was doing a periodic calculation.Saidel
C
11

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> 
Cushiony answered 25/2, 2014 at 19:8 Comment(0)
S
11

Here's another implementation, internationalized, written in TypeScript:

const UNITS = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte']
const BYTES_PER_KB = 1000


/**
 * Format bytes as human-readable text.
 *
 * @param sizeBytes Number of bytes.
 *
 * @return Formatted string.
 */
export function humanFileSize(sizeBytes: number | bigint): string {
    let size = Math.abs(Number(sizeBytes))

    let u = 0
    while(size >= BYTES_PER_KB && u < UNITS.length-1) {
        size /= BYTES_PER_KB
        ++u
    }

    return new Intl.NumberFormat([], {
        style: 'unit',
        unit: UNITS[u],
        unitDisplay: 'short',
        maximumFractionDigits: 1,
    }).format(size)
}

Replace [] with a language code like fr to force a localization other than the default.

console.log(humanFileSize(0))
console.log(humanFileSize(9))
console.log(humanFileSize(99))
console.log(humanFileSize(999))
console.log(humanFileSize(1000))
console.log(humanFileSize(1001))
console.log(humanFileSize(1023))
console.log(humanFileSize(1024))
console.log(humanFileSize(1025))
console.log(humanFileSize(100_000))
console.log(humanFileSize(1_000_000))
console.log(humanFileSize(1_000_000_000))
console.log(humanFileSize(1_000_000_000_000))
console.log(humanFileSize(1_000_000_000_000_000))
console.log(humanFileSize(1_000_000_000_000_000_000))
// fr
0 o
9 o
99 o
999 o
1 ko
1 ko
1 ko
1 ko
1 ko
100 ko
1 Mo
1 Go
1 To
1 Po
1 000 Po

// en-US
0 byte
9 byte
99 byte
999 byte
1 kB
1 kB
1 kB
1 kB
1 kB
100 kB
1 MB
1 GB
1 TB
1 PB
1,000 PB

You can get Intl.NumberFormat to do the unit conversion for you automatically now. e.g.

const sizeFormatter = new Intl.NumberFormat([], {
    style: 'unit',
    unit: 'byte',
    notation: "compact",
    unitDisplay: "narrow",
})

console.log(sizeFormatter.format(0))
console.log(sizeFormatter.format(1))
console.log(sizeFormatter.format(999))
console.log(sizeFormatter.format(1000))
console.log(sizeFormatter.format(1023))
console.log(sizeFormatter.format(1024))
console.log(sizeFormatter.format(1024**2))
console.log(sizeFormatter.format(1024**3))
console.log(sizeFormatter.format(1024**4))
console.log(sizeFormatter.format(1024**5))
console.log(sizeFormatter.format(1024**6))

...but the units are kinda weird. e.g. 1024**4 is 1.1BB which I guess is "billion bytes"; I don't think anyone ever uses that even if it's technically correct.

Shandrashandrydan answered 13/6, 2022 at 0:53 Comment(2)
I love how you fixed the "BB" problem!Crenel
This is the only answer that uses IntlAmity
D
10

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

Dusa answered 3/12, 2020 at 10:22 Comment(0)
C
8

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"
Commensurate answered 28/6, 2019 at 11:45 Comment(5)
using toFixed converts it to a string, so your round is either a string or a number. this is bad practice, you can easily convert it back to a number: +num.tofixed(2)Smutch
Does .toPrecision(3) not cover all these cases? Oh.. I guess it doesn't cover between 1000 and 1023. Bummer.Shandrashandrydan
It shows 9.9 for 10130 input, but should show 9.89. For 1023910.0, but should 9.99. (Win 10 Explorer behavior)Ungava
humanFileSize(10000000000000000000000000000) -> 8.08 undefinedB ahh my favourite kind of byteFalito
The most closed to Windows Explorer format function: gist.github.com/AlttiRi/ee82de3728624f997b38e4fb90906914Ungava
S
8

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

https://www.npmjs.com/package/file-size

Serpens answered 9/7, 2020 at 10:47 Comment(1)
Note: this library file-size is much less used and not really supported (last release 7yr ago). Suggest looking at filesize instead from my answer https://mcmap.net/q/92397/-converting-file-size-in-bytes-to-human-readable-stringDusa
E
6

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
Eddins answered 7/4, 2020 at 15:9 Comment(0)
T
5

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));
Tricia answered 2/7, 2020 at 19:4 Comment(0)
E
4

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;
}
Ecology answered 6/3, 2015 at 10:10 Comment(3)
It combines the performance hit of both the looping and the use of exponentiation, while being pretty hard to read. I don't really see the point.Threatt
Don't use it then. It's just clientside cpu thats used so who cares ;)Ecology
@Ecology Well, client CPU is precious too, especially on mobile and with complex applications. :)Levitation
W
4

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>'));
Waylay answered 11/8, 2016 at 13:24 Comment(0)
S
3
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.

Shalne answered 2/5, 2012 at 19:43 Comment(1)
@Eli... yea, it seems like it. I guess I was expecting "1.5" since its 1551859712, but that would mean I'm in decimal not binary.Gaughan
T
3

i'm just 10 years late! for es6

function humanReadableSize(bytes) {
    let size = parseInt(data)
    for (let unit of ['b', 'Kb', 'Mb', 'Gb']) {
        if (size < 1024) return `${size.toFixed(2)} ${unit}`
        size /= 1024.0
    }
}
Trackless answered 23/7, 2022 at 11:33 Comment(0)
I
2

I found @cocco's answer interesting, but had the following issues with it:

  1. Don't modify native types or types you don't own
  2. Write clean, readable code for humans, let minifiers optimize code for machines
  3. (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')
});
Ilailaire answered 16/12, 2019 at 23:47 Comment(0)
L
1

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
Lurk answered 29/4, 2020 at 15:35 Comment(0)
U
1

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]}`;
};
Uzia answered 24/4, 2022 at 9:29 Comment(0)
C
0

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

Link to the docs.

Chum answered 11/5, 2019 at 12:30 Comment(0)
S
0

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 toFixed() dynamically in js

Schwann answered 23/2, 2022 at 20:2 Comment(0)
M
0

I wrote a size conversion function that also accepts human readable formats.

JS

const bitBase = 8;
const suffixes = {
  bit: 'b',
  b: 'B',
  kb: 'KB',
  mb: 'MB',
  gb: 'GB',
  tb: 'TB',
};
const multipliers = {
  bit: {
    toBitHr: 1,
    toB: 1 / bitBase,
    toKB: 1 / (bitBase * 1e3),
    toMB: 1 / (bitBase * 1e6),
    toGB: 1 / (bitBase * 1e9),
    toTB: 1 / (bitBase * 1e12),
  },
  B: {
    toBit: bitBase,
    toBHr: 1,
    toKB: 1 / 1e3,
    toMB: 1 / 1e6,
    toGB: 1 / 1e9,
    toTB: 1 / 1e12,
  },
  KB: {
    toBit: 1 / (bitBase * 1e3),
    toB: 1e3,
    toKBHr: 1,
    toMB: 1 / 1e3,
    toGB: 1 / 1e6,
    toTB: 1 / 1e9,
  },
  MB: {
    toBit: bitBase * 1e6,
    toB: 1e6,
    toKB: 1e3,
    toMBHr: 1,
    toGB: 1 / 1e3,
    toTB: 1 / 1e6,
  },
  GB: {
    toBit: bitBase * 1e9,
    toB: 1e9,
    toKB: 1e6,
    toMB: 1e3,
    toGBHr: 1,
    toTB: 1 / 1e3,
  },
  TB: {
    toBit: bitBase * 1e12,
    toB: 1e12,
    toKB: 1e9,
    toMB: 1e6,
    toGB: 1e3,
    toTBHr: 1,
  },
};

const round = (num, decimalPlaces) => {
  const strNum = num.toString();
  const isExp = strNum.includes('e');
  if (isExp) {
    return Number(num.toPrecision(decimalPlaces + 1));
  }

  return Number(
    `${Math.round(Number(`${num}e${decimalPlaces}`))}e${decimalPlaces * -1}`,
  );
};

function conv(
  value,
  hr,
  rnd,
  multiplier,
  suffix,
) {
  let val = value * multiplier;
  if ((value * multiplier) > Number.MAX_SAFE_INTEGER) {
    val = Number.MAX_SAFE_INTEGER;
  }
  if (val < Number.MIN_VALUE) val = 0;
  if ((rnd || rnd === 0) && val < Number.MAX_SAFE_INTEGER) {
    val = round(val, rnd);
  }
  if (hr) return `${val}${suffix}`;
  return val;
}

const MemConv = (function _() {
  return {
    bit(value) {
      return {
        toBitHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.bit.toBitHr,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toTB,
            suffixes.tb,
          );
        },
      };
    },
    B(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toBit,
            suffixes.bit,
          );
        },
        toBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.B.toBHr,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toTB,
            suffixes.tb,
          );
        },
      };
    },
    KB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toB,
            suffixes.b,
          );
        },
        toKBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.KB.toKBHr,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toTB,
            suffixes.tb,
          );
        },
      };
    },
    MB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toKB,
            suffixes.kb,
          );
        },
        toMBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.MB.toMBHr,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toTB,
            suffixes.tb,
          );
        },
      };
    },
    GB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toMB,
            suffixes.mb,
          );
        },
        toGBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.GB.toGBHr,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toTB,
            suffixes.tb,
          );
        },
      };
    },
    TB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toGB,
            suffixes.gb,
          );
        },
        toTBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.TB.toTBHr,
            suffixes.tb,
          );
        },
      };
    },
  };
}());

const testCases = [1, 10, 150, 1000, 74839.67346];
const HRSuffixes = Object.values(suffixes);
const roundDecimals = 2;
const precision = Number(`0.${'0'.repeat(roundDecimals)}5`);
const SCIENTIFIC_NOT_NUMBER_REGXP = /[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)?/g;
const SUFFIX_REGXP = /[a-z]+$/i;
const CONVERSION_TO_REGXP = /(?<=to).*(?=hr+$)|(?<=to).*(?=hr+$)?/i;

for (const conversionFrom of (Object.keys(MemConv))) {
  for (const tCase of testCases) {
    const convFunc = MemConv[conversionFrom](tCase);
    for (const [conversionToFn, f] of Object.entries(convFunc)) {
      const conversionTo = (conversionToFn.match(CONVERSION_TO_REGXP) || [conversionToFn])[0];

      const result = f();
      const humanReadable = f({ hr: true });
      const rounded = f({ round: roundDecimals });
      const roundedAndHumanReadable = f({ hr: true, round: roundDecimals });


      console.log({
        value: tCase,
        from: conversionFrom,
        to: conversionTo,
        result,
        humanReadable,
        rounded,
        roundedAndHumanReadable,
      });
    }
  }
}

TSVersion

test

import assert from 'assert';

function test() {
  const testCases = [1, 10, 150, 1000, 74839.67346];
  const HRSuffixes = Object.values(suffixes);
  const roundDecimals = 2;
  const precision = Number(`0.${'0'.repeat(roundDecimals)}5`);
  const SCIENTIFIC_NOT_NUMBER_REGXP = /[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)?/g;
  const SUFFIX_REGXP = /[a-z]+$/i;
  const CONVERSION_TO_REGXP = /(?<=to).*(?=hr+$)|(?<=to).*(?=hr+$)?/i;

  for (const conversionFrom of (Object.keys(MemConv) as (keyof typeof MemConv)[])) {
    for (const tCase of testCases) {
      const convFunc = MemConv[conversionFrom](tCase);
      for (const [conversionToFn, f] of Object.entries(convFunc)) {
        const conversionTo = (conversionToFn.match(CONVERSION_TO_REGXP) || [conversionToFn])[0];
        const expectedSuffix = suffixes[conversionTo.toLowerCase() as keyof typeof suffixes];
        const multiplier = multipliers[conversionFrom][conversionToFn as keyof typeof multipliers[typeof conversionFrom]];
        const expectedResult = tCase * multiplier > Number.MAX_SAFE_INTEGER
            ? Number.MAX_SAFE_INTEGER
            : tCase * multiplier;

        const result = f();
        const humanReadable = f({ hr: true });
        const rounded = f({ round: roundDecimals });
        const roundedAndHumanReadable = f({ hr: true, round: roundDecimals });

        const resHrNumber = Number((humanReadable.match(SCIENTIFIC_NOT_NUMBER_REGXP) || [''])[0]);
        const resHrSuffix = (humanReadable.match(SUFFIX_REGXP) || [0])[0];
        const resRoundHrNumber = Number((roundedAndHumanReadable.match(SCIENTIFIC_NOT_NUMBER_REGXP) || [''])[0]);
        const resRoundHrSuffix = (roundedAndHumanReadable.match(SUFFIX_REGXP) || [0])[0];

        if (/hr$/i.test(conversionToFn)) {
          const resNumber = Number((humanReadable.match(SCIENTIFIC_NOT_NUMBER_REGXP) || [''])[0]);
          const resSuffix = (humanReadable.match(SUFFIX_REGXP) || [0])[0];
          assert(typeof result === 'string');
          assert(typeof resSuffix === 'string');
          assert(typeof resRoundHrNumber === 'number');
          assert(typeof rounded === 'string');
          assert(result === humanReadable);
          assert(resSuffix === expectedSuffix);
          assert(resNumber <= expectedResult + precision && resNumber >= expectedResult - precision);
        } else {
          assert(typeof result === 'number');
          assert(result === resHrNumber);
          assert(typeof rounded === 'number');
          assert(result <= expectedResult + precision && result >= expectedResult - precision);
        }

        console.log({
          value: tCase,
          from: conversionFrom,
          to: conversionToFn,
          result,
          humanReadable,
          rounded,
          roundedAndHumanReadable,
        });

        assert(typeof resHrSuffix === 'string');
        assert(typeof resHrNumber === 'number');
        assert(resHrSuffix === expectedSuffix);
        assert(resHrSuffix === resRoundHrSuffix);
        assert(HRSuffixes.includes(resHrSuffix));
      }
    }
  }
}
test();

Usage

// GB to GB humanReadable
console.log(MemConv.GB(11.1942).toGBHr()); // 11.1942GB;
// GB to MB
console.log(MemConv.GB(11.1942).toMB());// 11194.2;
// MB to MB humanReadable
console.log(MemConv.MB(11.1942).toGB({ hr: true }));// 0.011194200000000001GB;
// MB to MB humanReadable with rounding
console.log(MemConv.MB(11.1942).toGB({ hr: true, round: 3 }));// 0.011GB;
Milt answered 30/8, 2022 at 18:53 Comment(0)
N
-2

let bytes = 1024 * 10 * 10 * 10;

console.log(getReadableFileSizeString(bytes))

will return 1000.0Кб instead of 1MB

Neurath answered 6/12, 2018 at 2:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.