Convertir el tamaño del archivo en bytes a una cadena legible por humanos

239

Estoy usando esta función para convertir un tamaño de archivo en bytes a un tamaño de archivo legible para humanos:

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];
};

Sin embargo, parece que esto no es 100% exacto. Por ejemplo:

getReadableFileSizeString(1551859712); // output is "1.4 GB"

¿No debería ser esto "1.5 GB"? Parece que la división por 1024 está perdiendo precisión. ¿Estoy entendiendo totalmente algo mal o hay una mejor manera de hacer esto?

Hristo
fuente
3
getReadableFileSizeString (0); devuelve 0.1kb; p
Daniel Magnusson
2
¿Por qué debería ser 1.5? Es lo 1.445281982421875que se redondea correctamente a 1.4.
mpen
1
1551859712 / (1024 ^ 3) = 1.445281982421875 que es correcto!
HM
2
Me encanta que hayas añadido YB. Dudoso, cualquiera obtendrá incluso 1 YB por su DB. ¡Costará 100 billones de dólares !
guyarad
44
@guyarad: hay una imagen famosa de un disco duro de 5 MB de hace 50 años (era del tamaño de una habitación y pesaba aproximadamente una tonelada). Estoy seguro de que en ese entonces ni siquiera soñaban con GB y TB, y mira dónde estamos hoy ... nunca digas nunca ;-)
TheCuBeMan

Respuestas:

45

Depende de si desea utilizar la convención binaria o decimal.

La RAM, por ejemplo, siempre se mide en binario, por lo que expresar 1551859712 como ~ 1.4GiB sería correcto.

Por otro lado, a los fabricantes de discos duros les gusta usar decimal, por lo que lo llamarían ~ 1.6GB.

Y para ser confuso, los disquetes usan una mezcla de los dos sistemas: su 1 MB es en realidad 1024000 bytes.

Neil
fuente
3
cena divertida ;-) "solo para ser confuso, los disquetes usan una mezcla de los dos sistemas: su 1 MB es en realidad 1024000 bytes".
FranXho
Es cierto, los tamaños de RAM se miden con unidades IEC, los tamaños de disco con métrica ... hay un módulo npm isomorfo para convertir ambos: tamaño de byte
Lloyd
351

Aquí hay uno que escribí:

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

mpen
fuente
1
Estoy haciendo un ajuste: al evaluar el umbral, tome el valor absoluto. De esta manera, la función admitirá valores negativos. Buena función! ¡Gracias por no usar una declaración de cambio!
Aaron Blenkush
20
@AaronBlenkush: ¿Cuándo tendrías un tamaño de archivo negativo?
mpen
14
Acabo de copiar su función en una hoja de Google que estoy usando para mostrar el tamaño delta después de una operación de "limpieza". Antes, después y diferencia. La operación de limpieza resultó en el crecimiento de algunas tablas de bases de datos y la reducción en otras. Por ejemplo, la Tabla A tiene una diferencia de -1.95 MB, mientras que la Tabla B tiene una diferencia de 500 kB. Por lo tanto: positivo y negativo :-)
Aaron Blenkush
Aquí está la versión comprimida del 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]}
RAnders00
1
@ RAnders00: Gracias por la versión minificada. Sin embargo, ¿puede decirme por qué insertó los dos caracteres invisibles Unicode U + 200C (ANCHO CERO SIN UNIÓN) y U + 200B (ESPACIO ANCHO CERO) después de E oft EiB ? ¿Está destinado a ser una marca de agua, para que pueda rastrear quién usó este código? Si es así, creo que deberías haberlo hecho transparente en tu publicación.
Leviatán
81

Otra forma de realización del cálculo.

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];
};
Andrew V.
fuente
8
parece no maneja 0
Offirmo
44
¿Maneja o no maneja 0? Después de todo, esto con un if (size == 0) {} else {} es aún más elegante que la mayoría que he visto.
Rodrigo
13
Cambiar la primera línea var i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) );parece hacer el truco si es 0. Devolverá "0 B".
Gavin
Solo para tu información; Sé que la respuesta es JavaScript simple, pero si alguien quiere usarlo en TypeScript, no funciona (no está escrito correctamente, como lo estás haciendo toFixedy luego haciendo matemáticas con una cadena. ¿Qué hace * 1?
Frexuz
1
El *1cambia el tipo de datos de cadena a número, por lo que para el valor 1024se obtiene 1 kBen lugar de 1.00 kB. Puede hacer feliz a TypeScript haciendo Number((size / Math.pow(1024, i)).toFixed(2))lo mismo.
Adrian T
38

Aquí hay un prototipo para convertir un número en una cadena legible respetando los nuevos estándares internacionales.

Hay dos formas de representar números grandes: puede mostrarlos en múltiplos de 1000 = 10 3 (base 10) o 1024 = 2 10 (base 2). Si divide por 1000, probablemente use los nombres de prefijo SI, si divide por 1024, probablemente use los nombres de prefijo IEC. El problema comienza con la división por 1024. Muchas aplicaciones usan los nombres de prefijos SI y algunas usan los nombres de prefijos IEC. La situación actual es un desastre. Si ve nombres de prefijos SI, no sabe si el número está dividido entre 1000 o 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});

Esta función no contiene loop, por lo que probablemente sea más rápida que otras funciones.

Uso:

Prefijo IEC

console.log((186457865).fileSize()); // default IEC (power 1024)
//177.82 MiB
//KiB,MiB,GiB,TiB,PiB,EiB,ZiB,YiB

Prefijo SI

console.log((186457865).fileSize(1)); //1,true for SI (power 1000)
//186.46 MB 
//kB,MB,GB,TB,PB,EB,ZB,YB

configuré el IEC como predeterminado porque siempre usé el modo binario para calcular el tamaño de un archivo ... usando la potencia de 1024


Si solo quieres uno de ellos en una función de línea corta:

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

Uso:

console.log(fileSizeIEC(7412834521));

si tiene alguna pregunta sobre las funciones solo pregunte

coco
fuente
código compacto muy agradable, aunque personalmente agregaría un par de caracteres adicionales para el control de los lugares decimales.
Orwellophile
¡Hola! En realidad, el código es como lo escribí la primera vez en jsfiddle. En los últimos años aprendí a usar taquigrafía y bit a bit. Dispositivos móviles lentos, Internet lento, poco espacio ... al hacerlo, ahorré mucho tiempo. Pero eso no es todo, el rendimiento general aumentó drásticamente en todos los navegadores y todo el código se carga mucho más rápido ... No uso jquery, así que no tengo que cargar 100 kb cada vez. También necesito decir que escribo javascript también en microcontroladores, televisores inteligentes, consolas de juegos. los que tienen espacio limitado (MCU), rendimiento (SmartTV) y, naturalmente, a veces conexión lenta (Mobile)
cocco
Dijo que espero que entiendas mi elección. Todo lo que puedo hacer es explicar lo que no entiendes o, por otro lado, siempre estoy feliz de aprender cosas nuevas. Si hay algo en mi código que podría aumentar el rendimiento o ahorrar espacio, estoy feliz de escucharlo.
cocco
18
La minificación debe ser parte de su proceso de compilación, no su estilo de codificación. Ningún desarrollador serio usará este código debido a eso, ya que toma demasiado tiempo leer y verificar la corrección.
huysentruitw
1
Para aquellos que odian ver "15.00 Bytes", pueden modificar esta parte un poco:.toFixed(e ? 2 : 0)
Lukman
20
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"

Joshaven Potter
fuente
2
Si quería deshacerse del espacio adicional para bytes, se puede utilizar el espacio de ancho cero \u200b: '\u200bKMGTP'.
cdmckay
15

Solución como componente ReactJS

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

ACTUALIZACIÓN Para aquellos que usan es6 aquí hay una versión sin estado de este mismo componente

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,
};
Patrick Mencias-Lewis
fuente
1
Muchas gracias. Acaba de olvidar "bytes" dentro de Math.log () en la primera línea de la función
getBytes
Muy agradable. Para la desambiguación, y con la notación ES6, puede usar esto: return (! Bytes && '0 Bytes') || ${(bytes / (1024 ** i)).toFixed(2)} ${suffixes[i]};
Little Brain
12

Basado en la idea de Cocco , aquí hay un ejemplo menos compacto, pero con suerte más completo.

<!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> 
Kit Kat
fuente
8

Quería el comportamiento del "administrador de archivos" (por ejemplo, el Explorador de Windows) donde el número de lugares decimales es proporcional al tamaño del número. Al parecer, ninguna de las otras respuestas hace esto.

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

Aquí hay algunos ejemplos:

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"
Camilo Martin
fuente
El uso de toFixed lo convierte en una cadena, por lo que su ronda es una cadena o un número. esta es una mala práctica, puede convertirlo fácilmente a un número:+num.tofixed(2)
Vincent Duprez
¿ .toPrecision(3)No cubre todos estos casos? Oh ... supongo que no cubre entre 1000 y 1023. Bummer.
mpen
7

Otro ejemplo similar a los de aquí.

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';
}

Mide un rendimiento significativamente mejor que los otros con características similares.

Nick Kuznia
fuente
Esto proporciona un mejor rendimiento que algunas otras respuestas. Estoy usando esto Otros hicieron que mis pestañas de Chrome se colgaran y tomaran un 99.9% de CPU mientras hacía un cálculo periódico.
Nir Lanka
5

Aquí está el mío: también funciona para archivos realmente grandes -_-

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;
}
fiffy
fuente
Combina el éxito del rendimiento tanto del bucle como del uso de la exponenciación, a la vez que es bastante difícil de leer. Realmente no veo el punto.
espectros
2
No lo uses entonces. Solo se usa la CPU del lado del cliente, así que a quién le importa;)
fiffy
2
@fiffy Bueno, la CPU del cliente también es preciosa, especialmente en dispositivos móviles y con aplicaciones complejas. :)
Raito
5

Basado en la respuesta de Cocco pero ligeramente desugerificada (honestamente, las que me sentí cómodas se mantienen / agregan) y no muestran ceros finales, pero aún admiten 0, espero sean útiles para otros:

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

Ebrahim Byagowi
fuente
4
1551859712 / 1024 = 1515488
1515488 / 1024 = 1479.96875
1479.96875 / 1024 = 1.44528198242188

Tu solución es la correcta. Lo importante a tener en cuenta es que para pasar de 1551859712a 1.5, debe hacer divisiones por 1000, pero los bytes se cuentan en fragmentos binarios a decimales de 1024, de ahí que el valor de Gigabyte sea menor.

Eli
fuente
@Eli ... sí, parece que sí. Supongo que esperaba "1.5" desde su 1551859712, pero eso significaría que estoy en decimal no binario.
Hristo
3

Encontré la respuesta de @cocco interesante, pero tuve los siguientes problemas:

  1. No modifique los tipos nativos o tipos que no le pertenecen
  2. Escriba código limpio y legible para humanos, deje que los minificadores optimicen el código para máquinas
  3. (Bonificación para usuarios de TypeScript) No funciona bien con TypeScript

Mecanografiado:

 /**
 * 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')
});
moribvndvs
fuente
1

Esta es la mejora de tamaño de la respuesta mpen

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

Kamil Kiełczewski
fuente
0

Para aquellos que usan Angular, hay un paquete llamadoangular-pipes que tiene una tubería para esto:

Expediente

import { BytesPipe } from 'angular-pipes';

Uso

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

Enlace a los documentos .

Sinandro
fuente
0

Mi respuesta puede llegar tarde, pero supongo que ayudará a alguien.

Prefijo métrico:

/**
 * 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]}`;
};

Prefijo binario:

/**
 * 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]}`;
};

Ejemplos:

// 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
Kerkouch
fuente
-1

dejar bytes = 1024 * 10 * 10 * 10;

console.log (getReadableFileSizeString (bytes))

devolverá 1000.0Кб en lugar de 1MB

webolizzer
fuente