400

I got this code to covert size in bytes via PHP.

Now I want to convert those sizes to human readable sizes using JavaScript. I tried to convert this code to JavaScript, which looks like this:

function formatSizeUnits(bytes){
  if      (bytes >= 1073741824) { bytes = (bytes / 1073741824).toFixed(2) + " GB"; }
  else if (bytes >= 1048576)    { bytes = (bytes / 1048576).toFixed(2) + " MB"; }
  else if (bytes >= 1024)       { bytes = (bytes / 1024).toFixed(2) + " KB"; }
  else if (bytes > 1)           { bytes = bytes + " bytes"; }
  else if (bytes == 1)          { bytes = bytes + " byte"; }
  else                          { bytes = "0 bytes"; }
  return bytes;
}

Is this the correct way of doing this? Is there an easier way?

l2aelba
  • 20,261
  • 20
  • 97
  • 133

27 Answers27

1101

From this: (source)

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

Note: This is original code, Please use fixed version below.


Fixed version, unminified and ES6'ed: (by community)

function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

Fixed Version (by StackOverflow's community, minified by JSCompress)

function formatBytes(a,b=2,k=1024){with(Math){let d=floor(log(a)/log(k));return 0==a?"0 Bytes":parseFloat((a/pow(k,d)).toFixed(max(0,b)))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}}

Usage :

// formatBytes(bytes,decimals)

formatBytes(1024);       // 1 KB
formatBytes('1024');     // 1 KB
formatBytes(1234);       // 1.21 KB
formatBytes(1234, 3);    // 1.205 KB

Demo / source :

function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

// ** Demo code **
var p = document.querySelector('p'),
    input = document.querySelector('input');
    
function setText(v){
    p.innerHTML = formatBytes(v);
}
// bind 'input' event
input.addEventListener('input', function(){ 
    setText( this.value )
})
// set initial text
setText(input.value);
<input type="text" value="1000">
<p></p>

PS : Change k = 1000 or sizes = ["..."] as you want (bits or bytes)

AMJ
  • 121
  • 1
  • 6
Aliceljm
  • 11,041
  • 2
  • 15
  • 21
  • 10
    (1) why bytes = 0 is "n/a"? Isn't it just "0 B"? (2) Math.round doesn't have precision parameter. I'd better use `(bytes / Math.pow(1024, i)).toPrecision(3)` – disfated Sep 21 '13 at 15:50
  • A see two problems: First: values f.ex 1000. It returns 1.00e+3. This can be fixed with this: `Number((bytes / Math.pow(k, i)).toPrecision(3)) + ' ' + sizes[i];`. Second: values like 0.7. Logarithm returns negative value. It can be fixed with this: `if(bytes < 1) return '0 B';` – Ajax Oct 17 '14 at 17:40
  • Works nice ! there is another issue when approaching 1GB or 1TB values, it will show you 1000 MB or 1000GB instead, due to the approximation. a little fix is to add this before the last line : `if(String((bytes / Math.pow(k, i)).toFixed(0)).length === 4){i++;}` As @Ajax said, there is an issue with numbers returned as 1.00e+3. I would suggest to use `toFixed(x)` instead of `toPrecision(x)` to fix the number of decimals. `toPrecision(x)` define the total characters of your value. (that's where the 1.00e+3 comes from, since it cant write 1000, with a precision of 3.) – hereismass Feb 12 '15 at 15:20
  • 4
    `toFixed(n)` is probably more appropriate than `toPrecision(n)` to have a consistant precision for all the values. And to avoid trailing zeros (ex: `bytesToSize(1000) // return "1.00 KB"`) we could use `parseFloat(x)`. I suggest to replace the last line by: `return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];`. With the previous change the results are: `bytesToSize(1000) // return "1 KB"` / `bytesToSize(1100) // return "1.1 KB"` / `bytesToSize(1110) // return "1.11 KB` / `bytesToSize(1111) // also return "1.11 KB"` – MathieuLescure Mar 25 '15 at 15:19
  • Need correction: to handle -ve Math.floor(Math.log(Math.abs(bytes)) – jaaw Dec 22 '15 at 01:33
  • `999999999999999999999999990` returns `1 undefined` – Slava Mar 19 '16 at 13:42
  • @Alph.Dev add your more `var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];` – l2aelba Apr 05 '16 at 19:41
  • 4
    I believe plural form is used for 0: '0 Bytes' – nima May 05 '16 at 09:41
  • 1
    I made some modifications to be able to manage non-decimal returned values:```let dm: number = decimals !== undefined ? decimals : 2;```. In any case, great snippet! – adripanico Jun 21 '16 at 14:40
  • Just a tiny modification: add radix value 10 to `parseInt` — "Always specify this parameter to eliminate reader confusion and to guarantee predictable behavior. Different implementations produce different results when a radix is not specified, usually defaulting the value to 10" via https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/parseInt – Mikael Korpela Jul 24 '16 at 19:24
  • Thanks @l2aelba ,I'm busy getting married,Now I am back :) – Aliceljm May 16 '17 at 09:58
  • 25
    I'd say minify is nice, but in a stackexchange answer it is better to have the more verbose and readable code. – donquixote Jun 01 '17 at 17:12
  • @donquixote Minify verion is from **Demo / source** section – l2aelba Jun 06 '17 at 12:59
  • 5
    KB = Kelvin bytes in SI units. which is nonsensical. It should be kB. – Brennan T Jul 10 '17 at 21:33
  • 1
    The "fixed by the community" version divides by 1000 instead of 1024. That's a really important bug here. – Jorge Fuentes González Aug 02 '17 at 08:28
  • 1
    I've submitted an edit to your answer to add support for 0 decimals: `dm = typeof decimals != 'undefined' ? decimals : 2` instead of `dm = decimals || 2,` as your original line treated the "0" in 0 decimals like "false". – LWC Sep 22 '17 at 10:21
  • 2
    I don't think we should show a "minimized and compressed" version in a help topic. I recommend we uniminize/uncompress that. I added this in to the topic. – Noitidart Mar 30 '19 at 15:03
  • This fails --> Math.log(bytes) as it returns a negative value when the value of bytes is less than 1 (say 0.5) and – Devdutta Natu May 09 '19 at 15:37
  • How about adding `.toLocaleString()` so it gets convert to a user-friendly number. I'd also like to convert the return value into a template string, like this instead: return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm)).toLocaleString()} ${sizes[i]}` – dschu May 23 '19 at 08:54
  • It seems Apple uses 1000 instead of 1024. When testing a local file, the size reported by macOS is 472.3 MB. If `k = 1024` the size reported by the function is 450.45 MB. Changing it to `k = 1000` reports a size of 472.33 MB. – I0_ol Nov 19 '20 at 10:00
  • Why is this consistently incorrect? A size of 881,841 bytes prints out 861.17KB, A size of 2,651,236 bytes prints out 2.53 MB, and a size of 1,853,605 bytes prints out 1.77MB. These are just three examples, but it's consistently off. – Dan Zuzevich Jun 08 '21 at 16:39
  • ^ I am using the fixed es6 community version btw. – Dan Zuzevich Jun 08 '21 at 16:46
  • sometimes it displays 1000MB instead of 1GB – Gie Aug 06 '21 at 15:07
54

This solution includes a decimal point and a tenths-place digit if presenting less than ten of KB or greater units

const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
   
function niceBytes(x){

  let l = 0, n = parseInt(x, 10) || 0;

  while(n >= 1024 && ++l){
      n = n/1024;
  }
  
  return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
}

Results:

niceBytes(435)                 // 435 bytes
niceBytes(3398)                // 3.3 KB
niceBytes(490398)              // 479 KB
niceBytes(6544528)             // 6.2 MB
niceBytes(23483023)            // 22 MB
niceBytes(3984578493)          // 3.7 GB
niceBytes(30498505889)         // 28 GB
niceBytes(9485039485039445)    // 8.4 PB
Faust
  • 14,321
  • 9
  • 52
  • 109
  • 1
    *Thanks!* **Compressed:** `function niceBytes(a){let b=0,c=parseInt(a,10)||0;for(;1024<=c&&++b;)c/=1024;return c.toFixed(10>c&&0 – ashleedawg Nov 16 '20 at 10:50
49
function formatBytes(bytes) {
    var marker = 1024; // Change to 1000 if required
    var decimal = 3; // Change as required
    var kiloBytes = marker; // One Kilobyte is 1024 bytes
    var megaBytes = marker * marker; // One MB is 1024 KB
    var gigaBytes = marker * marker * marker; // One GB is 1024 MB
    var teraBytes = marker * marker * marker * marker; // One TB is 1024 GB

    // return bytes if less than a KB
    if(bytes < kiloBytes) return bytes + " Bytes";
    // return KB if less than a MB
    else if(bytes < megaBytes) return(bytes / kiloBytes).toFixed(decimal) + " KB";
    // return MB if less than a GB
    else if(bytes < gigaBytes) return(bytes / megaBytes).toFixed(decimal) + " MB";
    // return GB if less than a TB
    else return(bytes / gigaBytes).toFixed(decimal) + " GB";
}
Jayram
  • 17,790
  • 6
  • 49
  • 68
30

Here's a one liner:

val => ['Bytes','Kb','Mb','Gb','Tb'][Math.floor(Math.log2(val)/10)]

Or even:

v => 'BKMGT'[~~(Math.log2(v)/10)]

With count:

function shortenBytes(n) {
    k = n > 0 ? Math.floor((Math.log2(n)/10)) : 0;
    rank = (k > 0 ? 'KMGT'[k - 1] : '') + 'b';
    count = Math.floor(n / Math.pow(1024, k));
    return count + rank;
}
iDaN5x
  • 546
  • 8
  • 12
  • 1
    Nice !, But if 1k is 1024 not 1000 ? – l2aelba Feb 23 '17 at 14:41
  • 2
    This calculation *is* treating 1k as 2^10, 1m as 2^20 and so on. if you want 1k to be 1000 you can change it a bit to use log10. – iDaN5x Feb 24 '17 at 20:24
  • 1
    Here's a version that treats 1K as 1000: `val => 'BKMGT'[~~(Math.log10(val)/3)]` – iDaN5x Feb 25 '17 at 12:08
  • 2
    This is nice! I expanded on this to return the full string I wanted from my function: `i = ~~(Math.log2(b)/10); return (b/Math.pow(1024,i)).toFixed(2) + ("KMGTPEZY"[i-1]||"") + "B"` – v0rtex Sep 06 '18 at 15:54
17

You can use the filesizejs library.

maurocchi
  • 632
  • 6
  • 14
  • I guess this library gives the accurate representation, since 1024 bytes is 1 KB, not 1000 bytes (as provided by some other solutions here). Thanks @maurocchi – W.M. Aug 12 '16 at 15:04
  • 4
    @W.M. that statement is not true. 1kB = 1000 bytes. There are 1024 bytes in a Kibibyte. There has been confusion in the past so these two terms precisely explain the difference in size. – Brennan T Jul 10 '17 at 21:22
  • 2
    @BrennanT It depends how old you are. 1KB used to be 1024 bytes and most people over a certain age still see it as such. – kojow7 Nov 19 '17 at 21:46
  • 1
    for people who come here for a lib, here is better one: https://www.npmjs.com/package/pretty-bytes – ZuBB Oct 21 '21 at 09:18
16

There are 2 real ways to represent sizes when related to bytes, they are SI units (10^3) or IEC units (2^10). There is also JEDEC but their method is ambiguous and confusing. I noticed the other examples have errors such as using KB instead of kB to represent a kilobyte so I decided to write a function that will solve each of these cases using the range of currently accepted units of measure.

There is a formatting bit at the end that will make the number look a bit better (at least to my eye) feel free to remove that formatting if it doesn't suit your purpose.

Enjoy.

// pBytes: the size in bytes to be converted.
// pUnits: 'si'|'iec' si units means the order of magnitude is 10^3, iec uses 2^10

function prettyNumber(pBytes, pUnits) {
    // Handle some special cases
    if(pBytes == 0) return '0 Bytes';
    if(pBytes == 1) return '1 Byte';
    if(pBytes == -1) return '-1 Byte';

    var bytes = Math.abs(pBytes)
    if(pUnits && pUnits.toLowerCase() && pUnits.toLowerCase() == 'si') {
        // SI units use the Metric representation based on 10^3 as a order of magnitude
        var orderOfMagnitude = Math.pow(10, 3);
        var abbreviations = ['Bytes', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    } else {
        // IEC units use 2^10 as an order of magnitude
        var orderOfMagnitude = Math.pow(2, 10);
        var abbreviations = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
    }
    var i = Math.floor(Math.log(bytes) / Math.log(orderOfMagnitude));
    var result = (bytes / Math.pow(orderOfMagnitude, i));

    // This will get the sign right
    if(pBytes < 0) {
        result *= -1;
    }

    // This bit here is purely for show. it drops the percision on numbers greater than 100 before the units.
    // it also always shows the full number of bytes if bytes is the unit.
    if(result >= 99.995 || i==0) {
        return result.toFixed(0) + ' ' + abbreviations[i];
    } else {
        return result.toFixed(2) + ' ' + abbreviations[i];
    }
}
Brennan T
  • 169
  • 1
  • 4
6

Utility method to format bytes into the most logical magnitude (KB, MB, or GB)

Number.prototype.formatBytes = function() {
    var units = ['B', 'KB', 'MB', 'GB', 'TB'],
        bytes = this,
        i;
 
    for (i = 0; bytes >= 1024 && i < 4; i++) {
        bytes /= 1024;
    }
 
    return bytes.toFixed(2) + units[i];
}

let a = 235678; //bytes

console.log(a.formatBytes()); //result is 230.15KB
Matee Gojra
  • 4,208
  • 2
  • 27
  • 31
5

This works for me.

bytesForHuman(bytes) {
    let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']

    let i = 0
    
    for (i; bytes > 1024; i++) {
        bytes /= 1024;
    }

    return bytes.toFixed(1) + ' ' + units[i]
}
Zayar Tun
  • 121
  • 2
  • 5
4

Using bitwise operation would be a better solution. Try this

function formatSizeUnits(bytes)
{
    if ( ( bytes >> 30 ) & 0x3FF )
        bytes = ( bytes >>> 30 ) + '.' + ( bytes & (3*0x3FF )) + 'GB' ;
    else if ( ( bytes >> 20 ) & 0x3FF )
        bytes = ( bytes >>> 20 ) + '.' + ( bytes & (2*0x3FF ) ) + 'MB' ;
    else if ( ( bytes >> 10 ) & 0x3FF )
        bytes = ( bytes >>> 10 ) + '.' + ( bytes & (0x3FF ) ) + 'KB' ;
    else if ( ( bytes >> 1 ) & 0x3FF )
        bytes = ( bytes >>> 1 ) + 'Bytes' ;
    else
        bytes = bytes + 'Byte' ;
    return bytes ;
}
Buzz LIghtyear
  • 450
  • 5
  • 16
4

According to Aliceljm's answer, I removed 0 after decimal:

function formatBytes(bytes, decimals) {
    if(bytes== 0)
    {
        return "0 Byte";
    }
    var k = 1024; //Or 1 kilo = 1000
    var sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
    var i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + " " + sizes[i];
}
Community
  • 1
  • 1
abla
  • 324
  • 4
  • 7
4

function bytesToSize(bytes) {
  var sizes = ['B', 'K', 'M', 'G', 'T', 'P'];
  for (var i = 0; i < sizes.length; i++) {
    if (bytes <= 1024) {
      return bytes + ' ' + sizes[i];
    } else {
      bytes = parseFloat(bytes / 1024).toFixed(2)
    }
  }
  return bytes + ' P';
}

console.log(bytesToSize(234));
console.log(bytesToSize(2043));
console.log(bytesToSize(20433242));
console.log(bytesToSize(2043324243));
console.log(bytesToSize(2043324268233));
console.log(bytesToSize(2043324268233343));
Ruslan López
  • 4,289
  • 1
  • 25
  • 36
jasee
  • 49
  • 4
3

This solution builds upon previous solutions, but takes into account both metric and binary units:

function formatBytes(bytes, decimals, binaryUnits) {
    if(bytes == 0) {
        return '0 Bytes';
    }
    var unitMultiple = (binaryUnits) ? 1024 : 1000; 
    var unitNames = (unitMultiple === 1024) ? // 1000 bytes in 1 Kilobyte (KB) or 1024 bytes for the binary version (KiB)
        ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']: 
        ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    var unitChanges = Math.floor(Math.log(bytes) / Math.log(unitMultiple));
    return parseFloat((bytes / Math.pow(unitMultiple, unitChanges)).toFixed(decimals || 0)) + ' ' + unitNames[unitChanges];
}

Examples:

formatBytes(293489203947847, 1);    // 293.5 TB
formatBytes(1234, 0);   // 1 KB
formatBytes(4534634523453678343456, 2); // 4.53 ZB
formatBytes(4534634523453678343456, 2, true));  // 3.84 ZiB
formatBytes(4566744, 1);    // 4.6 MB
formatBytes(534, 0);    // 534 Bytes
formatBytes(273403407, 0);  // 273 MB
TxRegex
  • 2,287
  • 18
  • 20
2

I originally used @Aliceljm's answer for a file upload project I was working on, but recently ran into an issue where a file was 0.98kb but being read as 1.02mb. Here's the updated code I'm now using.

function formatBytes(bytes){
  var kb = 1024;
  var ndx = Math.floor( Math.log(bytes) / Math.log(kb) );
  var fileSizeTypes = ["bytes", "kb", "mb", "gb", "tb", "pb", "eb", "zb", "yb"];

  return {
    size: +(bytes / kb / kb).toFixed(2),
    type: fileSizeTypes[ndx]
  };
}

The above would then be called after a file was added like so

// In this case `file.size` equals `26060275` 
formatBytes(file.size);
// returns `{ size: 24.85, type: "mb" }`

Granted, Windows reads the file as being 24.8mb but I'm fine with the extra precision.

Community
  • 1
  • 1
theOneWhoKnocks
  • 592
  • 6
  • 13
2
const byteConversion = (bytes: number, decimals = 2) => {
if (bytes === 0) return '0 B';

const kiloByte = 1000;
const decimal = decimals < 0 ? 0 : decimals;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

const i: number = Math.floor(Math.log(bytes) / Math.log(kiloByte));

return `${parseFloat((bytes / kiloByte ** i).toFixed(decimal))} ${sizes[i]}`;

};

ali tuna
  • 21
  • 2
1

I am updating @Aliceljm answer here. Since the decimal place matters for 1,2 digit numbers, I am round off the first decimal place and keep the first decimal place. For 3 digit number, I am round off the units place and ignoring the all decimal places.

getMultiplers : function(bytes){
    var unit = 1000 ;
    if (bytes < unit) return bytes ;
    var exp = Math.floor(Math.log(bytes) / Math.log(unit));
    var pre = "kMGTPE".charAt(exp-1);
    var result = bytes / Math.pow(unit, exp);
    if(result/100 < 1)
        return (Math.round( result * 10 ) / 10) +pre;
    else
        return Math.round(result) + pre;
}
omkar
  • 394
  • 3
  • 7
1

var SIZES = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

function formatBytes(bytes, decimals) {
  for(var i = 0, r = bytes, b = 1024; r > b; i++) r /= b;
  return `${parseFloat(r.toFixed(decimals))} ${SIZES[i]}`;
}
1

I just wanted to share my input. I had this problem so my solution is this. This will convert lower units to higher units and vice versa just supply the argument toUnit and fromUnit

export function fileSizeConverter(size: number, fromUnit: string, toUnit: string ): number | string {
  const units: string[] = ['B', 'KB', 'MB', 'GB', 'TB'];
  const from = units.indexOf(fromUnit.toUpperCase());
  const to = units.indexOf(toUnit.toUpperCase());
  const BASE_SIZE = 1024;
  let result: number | string = 0;

  if (from < 0 || to < 0 ) { return result = 'Error: Incorrect units'; }

  result = from < to ? size / (BASE_SIZE ** to) : size * (BASE_SIZE ** from);

  return result.toFixed(2);
}

I got the idea from here

aRtoo
  • 1,333
  • 1
  • 13
  • 31
1

I use Recursive and assign level variable for proper Unit.

function getReadableByte(count, decimal=0, level=0) {
    let unitList = ["Bytes", "KB", "MB", "GB", "TB", "PT"];

    if (count >= 1024.0 && (level+1 < unitList.length)) {
        return getReadableByte(count/1024, decimal, ++level)
    }
    return `${decimal ? (count).toFixed(decimal) : Math.round(count)}${unitList[level]}`
}

console.log(getReadableByte(120, 2))
  • 2
    Would help to explanain your solution (think of beginners reading it). Especially the more elegant but intermediate-level constructs like (a) unit-symbols as array, (b) recursion, (c) template-string. – hc_dev Jul 01 '21 at 23:13
1

Here is a solid Effective way to convert bytes . Only thing you need is to install mathjs library for accurate calculation . Just copy paste .

import { multiply, divide, round } from "mathjs";

class Size {
  constructor(value, unit) {
    this.value = value;
    this.unit = unit.toUpperCase();
  }
}

async function byteToSize(bytes) {
  const B = 1;
  const KB = multiply(B, 1024);
  const MB = multiply(KB, 1024);
  const GB = multiply(MB, 1024);
  const TB = multiply(GB, 1024);
  const PB = multiply(TB, 1024);

  if (bytes <= KB) {
    // @returns BYTE

    const result = round(divide(bytes, B));
    const unit = `B`;

    return new Size(result, unit);
  }

  if (bytes <= MB) {
    // @returns KILOBYTE

    const result = round(divide(bytes, KB));
    const unit = `KB`;

    return new Size(result, unit);
  }

  if (bytes <= GB) {
    // @returns MEGABYTE

    const result = round(divide(bytes, MB));
    const unit = `MB`;

    return new Size(result, unit);
  }

  if (bytes <= TB) {
    // @returns GIGABYTE

    const result = round(divide(bytes, GB));
    const unit = `GB`;

    return new Size(result, unit);
  }

  if (bytes <= PB) {
    // @returns TERABYTE

    const result = divide(bytes, TB).toFixed(2);
    const unit = `TB`;

    return new Size(result, unit);
  }

  if (bytes >= PB) {
    // @returns PETABYTE

    const result = divide(bytes, PB).toFixed(2);
    const unit = `PB`;

    return new Size(result, unit);
  }
}
1

I made a algorithm 7 times faster (works like a ninja):

function rafaelFormatBytes(number){
  if(number == null || number === undefined || number <= 0) {
    return '0 Bytes';
  }
  var scaleCounter = 0;
  var scaleInitials = [' Bytes',' KB',' MB',' GB',' TB',' PB',' EB',' ZB',' YB'];
  while (number >= 1024 && scaleCounter < scaleInitials.length - 1){
    number /= 1024;
    scaleCounter++;
  }
  if(scaleCounter >= scaleInitials.length) scaleCounter = scaleInitials.length - 1;
  var compactNumber = number.toFixed(2)
                              .replace(/\.?0+$/,'')
                              .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  compactNumber += scaleInitials[scaleCounter];
  return compactNumber.trim();
}
var testNum = 0;

var startTime, endTime;

function start() {
  startTime = new Date();
};

function end() {
  endTime = new Date();
  var timeDiff = endTime - startTime; //in ms
  // strip the ms
  timeDiff /= 1000;

  // get seconds 
  var seconds = Math.round(timeDiff, 5);
  console.log(timeDiff + " seconds");
}

function formatBytes(a,b=2,k=1024){with(Math){let d=floor(log(a)/log(k));return 0==a?"0 Bytes":parseFloat((a/pow(k,d)).toFixed(max(0,b)))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}}

console.log(formatBytes(1000000000000000000000000000));
console.log(rafaelFormatBytes(1000000000000000000000000000));

start();
for(i=0; i<100000; i++){
    formatBytes(1000000000000000);
}
end();
start();
for(i=0; i<100000; i++){
    rafaelFormatBytes(1000000000000000);
}
end();

... and the output:

827.18 YB
827.18 YB
0.293 seconds
0.039 seconds

Jesus Christ!!!!

1

Just slightly modified the code of @zayarTun answer to include an extra parameter representing the number of decimals in the result (also if the decimals are zero then no need to show a result like 15.00 KB, instead 15 KB is sufficient, that's why I wrapped the result value in parseFloat())

  bytesForHuman(bytes, decimals = 2) {
    let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']

    let i = 0
    
    for (i; bytes > 1024; i++) {
        bytes /= 1024;
    }

    return parseFloat(bytes.toFixed(decimals)) + ' ' + units[i]
  }
medBouzid
  • 6,732
  • 8
  • 49
  • 78
0

This is how a byte should be shown to a human:

function bytesToHuman(bytes, decimals = 2) {
  // https://en.wikipedia.org/wiki/Orders_of_magnitude_(data)
  const units = ["bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; // etc

  let i = 0;
  let h = 0;

  let c = 1 / 1023; // change it to 1024 and see the diff

  for (; h < c && i < units.length; i++) {
    if ((h = Math.pow(1024, i) / bytes) >= c) {
      break;
    }
  }

  // remove toFixed and let `locale` controls formatting
  return (1 / h).toFixed(decimals).toLocaleString() + " " + units[i];
}

// test
for (let i = 0; i < 9; i++) {
  let val = i * Math.pow(10, i);
  console.log(val.toLocaleString() + " bytes is the same as " + bytesToHuman(val));

}

// let's fool around
console.log(bytesToHuman(1023));
console.log(bytesToHuman(1024));
console.log(bytesToHuman(1025));
Eugen Mihailescu
  • 3,268
  • 2
  • 28
  • 28
0
function bytes2Size(byteVal){
    var units=["Bytes", "KB", "MB", "GB", "TB"];
    var kounter=0;
    var kb= 1024;
    var div=byteVal/1;
    while(div>=kb){
        kounter++;
        div= div/kb;
    }
    return div.toFixed(1) + " " + units[kounter];
}
Kjut
  • 36
  • 4
  • This function is easy-to-understand and follow - you can implement it in any language. It's a repeated division of the byte value until you attain the byte level (unit) that is greater than 1kb – Kjut Apr 11 '20 at 03:27
  • Just a quick note, There are differences between binary prefixes. Some follow SI base 10 rule and some follow base 2. You can read more [here](https://cseducators.stackexchange.com/a/4446). However, if you consider k to be 1024, instead of dividsion, you can simply use [shift operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift) like `byteVal >> 10` . Also [you would better use `Math.trunc()` to cast real numbers to integers](https://stackoverflow.com/a/596503/3994502) instead ofdivision by 1. – Cunning Apr 11 '20 at 05:59
  • Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually of higher quality, and are more likely to attract upvotes. – Mark Rotteveel Apr 11 '20 at 07:09
0

ONE-LINER

const b2s=t=>{let e=Math.log2(t)/10|0;return(t/1024**(e=e<=0?0:e)).toFixed(3)+"BKMGP"[e]};

console.log(b2s(0));
console.log(b2s(123));
console.log(b2s(123123));
console.log(b2s(123123123));
console.log(b2s(123123123123));
console.log(b2s(123123123123123));
nkitku
  • 3,348
  • 24
  • 21
0

Same answer from @Aliceljm , but in a "more didactic" way. Thanks! =D

function formatBytes(numBytes, decPlaces) {
    /* Adjust the number of bytes informed for the most appropriate metric according
    to its value.

    Args:
        numBytes (number): The number of bytes (integer);
        decPlaces (Optional[number])): The number of decimal places (integer). If
            it is "undefined" the value "2" will be adopted.

    Returns:
        string: The number adjusted together with the most appropriate metric. */

    if (numBytes === 0) {
        return "0 Bytes";
    }

    // NOTE: 1 KB is equal to 1024 Bytes. By Questor
    // [Ref(s).: https://en.wikipedia.org/wiki/Kilobyte ]
    var oneKByte = 1024;

    // NOTE: Treats if the "decPlaces" is "undefined". If it is "undefined" the value
    // "2" will be adopted. By Questor
    if (decPlaces === undefined || decPlaces === "") {
        decPlaces = 2;
    }

    var byteMtrcs = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
    // Byte Metrics

    // NOTE: Defines the factor for the number of bytes and the metric. By Questor
    var mtrcNumbFactor = Math.floor(Math.log(numBytes) / Math.log(oneKByte));
    // Metrics Number Factor

    return parseFloat((numBytes / Math.pow(oneKByte, mtrcNumbFactor)).
            toFixed(decPlaces)) + " " + byteMtrcs[mtrcNumbFactor];
}
Eduardo Lucio
  • 1,145
  • 1
  • 17
  • 32
0

I had a problem of meta data on files returning from the server with different size units. I used @Alicejim response and tried to do it more general. sharing the code here, maybe it will help someone.

enum SizeUnits {
   Bytes = 'Bytes',
   KB = 'KB',
   MB = 'MB',
   GB = 'GB',
   TB = 'TB',
   PB = 'PB',
   EB = 'EB',
   ZB = 'ZB',
   YB = 'YB'
}
function convertSizeUnites(size: number, sourceSizeUnits: SizeUnits, targetSizeUnits: SizeUnits) {
    const i = Object.keys(SizeUnits).indexOf(sourceSizeUnits);
    const sizeInBytes = size * Math.pow(1024, i);
    const j = Object.keys(SizeUnits).indexOf(targetSizeUnits);
    return sizeInBytes / Math.pow(1024, j);
}
function formatSize(size: number, measureUnit: SizeUnits, decimals = 2) {
    if (size === 0) return '0 Bytes';
    const sizeInBytes = convertSizeUnites(size, measureUnit, SizeUnits.Bytes);
    const dm = decimals < 0 ? 0 : decimals;
    const i = Math.floor(Math.log(sizeInBytes) / Math.log(1024));
    return parseFloat((sizeInBytes / Math.pow(1024, i)).toFixed(dm)) + ' ' + 
    Object.keys(SizeUnits)[i];
}
tomer
  • 9
  • 1
  • 1
-8

Try this simple workaround.

var files = $("#file").get(0).files;               
                var size = files[0].size;
                if (size >= 5000000) {
alert("File size is greater than or equal to 5 MB");
}
Liladhar
  • 385
  • 5
  • 19