Conversione delle dimensioni del file in byte in stringa leggibile dall'uomo


239

Sto usando questa funzione per convertire una dimensione del file in byte in una dimensione del file leggibile dall'uomo:

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

Tuttavia, sembra che questo non sia preciso al 100%. Per esempio:

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

Non dovrebbe essere "1.5 GB"? Sembra che la divisione per 1024 stia perdendo precisione. Sono totalmente frainteso qualcosa o c'è un modo migliore per farlo?


3
getReadableFileSizeString (0); restituisce 0.1kb; p
Daniel Magnusson il

2
Perché dovrebbe essere 1.5? È quello 1.445281982421875che arrotonda correttamente a 1.4.
aprono il

1
1551859712 / (1024 ^ 3) = 1.445281982421875 che è corretto!
SM

2
Adoro che tu abbia aggiunto YB. Qualcuno in dubbio otterrà anche 1 YB per il suo DB. Costerà 100 trilioni di dollari !
Guyarad,

4
@guyarad - c'è una famosa foto di un disco rigido da 5 MB di 50 anni fa (era delle dimensioni di una stanza e pesava circa una tonnellata). sono sicuro che all'epoca non
sognavano

Risposte:


45

Dipende se si desidera utilizzare la convenzione binaria o decimale.

La RAM, ad esempio, viene sempre misurata in binario, quindi per esprimere 1551859712 come ~ 1.4GiB sarebbe corretto.

D'altra parte, ai produttori di hard disk piace usare i decimali, quindi lo chiamerebbero ~ 1.6GB.

E solo per confondere, i floppy disk usano una combinazione dei due sistemi: il loro 1 MB è in realtà 1024000 byte.


3
cena divertente ;-) "solo per essere confusi, i floppy disk usano una combinazione dei due sistemi - il loro 1 MB è in realtà 1024000 byte."
FranXho,

vero, le dimensioni della RAM sono misurate usando unità IEC, le dimensioni del disco usando la metrica .. c'è un modulo npm isomorfo per convertire entrambi: dimensione dei byte
Lloyd,

351

Eccone uno che ho scritto:

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


1
Sto effettuando un aggiustamento: quando valuti la soglia, prendi il valore assoluto. In questo modo la funzione supporterà valori negativi. Bella funzione! Grazie per non aver usato un'istruzione switch !!
Aaron Blenkush,

20
@AaronBlenkush: quando avresti una dimensione del file negativa?
mpen

14
Ho appena copiato la tua funzione in un foglio di Google che sto usando per mostrare le dimensioni delta dopo un'operazione di "pulizia". Prima, dopo e diff. L'operazione di pulizia ha comportato la crescita di alcune tabelle del database e la riduzione di altre. Ad esempio, la tabella A ha una differenza di -1,95 MB, mentre la tabella B ha una differenza di 500 kB. Pertanto: positivo e negativo :-)
Aaron Blenkush

Ecco la versione compressa dello 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: grazie per la versione ridotta. Puoi dirmi perché hai inserito i due caratteri Unicode invisibili U + 200C (ZERO WIDTH NON-JOINER) e U + 200B (ZERO WIDTH SPACE) dopo E oft EiB ? Si intende che sia una filigrana, in modo da poter tenere traccia di chi ha utilizzato questo codice? In tal caso, penso che avresti dovuto renderlo trasparente nel tuo post.
Leviatano,

81

Un'altra forma di realizzazione del calcolo

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

8
sembra non gestire 0
Offirmo

4
Gestisce o non gestisce 0? Dopotutto, questo con un if (size == 0) {} else {} è ancora più elegante della maggior parte che abbia mai visto.
Rodrigo,

13
Cambiare la prima riga in var i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) );sembra fare il trucco se è 0. Restituirà "0 B".
Gavin,

Cordiali saluti; So che la risposta è semplice JavaScript, ma se qualcuno non vuole usarlo in TypeScript, non funziona (non digitato correttamente, come stai facendo toFixede poi facendo matematica con una stringa. Cosa fa * 1?
Frexuz

1
Il *1cambia il tipo di dati da stringa a numero, in modo per il valore 1024che si ottiene 1 kB, invece di 1.00 kB. Puoi rendere felice TypeScript facendo Number((size / Math.pow(1024, i)).toFixed(2))per ottenere lo stesso risultato.
Adrian T,

38

Ecco un prototipo per convertire un numero in una stringa leggibile rispettando i nuovi standard internazionali.

Esistono due modi per rappresentare numeri grandi: è possibile visualizzarli in multipli di 1000 = 10 3 (base 10) o 1024 = 2 10 (base 2). Se dividi per 1000, probabilmente usi i nomi dei prefissi SI, se dividi per 1024, probabilmente usi i nomi dei prefissi IEC. Il problema inizia con la divisione per 1024. Molte applicazioni usano i nomi dei prefissi SI e alcuni usano i nomi dei prefissi IEC. La situazione attuale è un casino. Se vedi i nomi dei prefissi SI non sai se il numero è diviso per 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});

Questa funzione non contiene loop, e quindi probabilmente è più veloce di alcune altre funzioni.

Uso:

Prefisso IEC

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

Prefisso SI

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

ho impostato IEC come predefinito perché ho sempre usato la modalità binaria per calcolare la dimensione di un file ... usando la potenza di 1024


Se vuoi solo uno di loro in una breve funzione di linea:

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

se hai qualche domanda sulle funzioni basta chiedere


codice compatto molto bello, aggiungerei personalmente un paio di caratteri extra per il controllo dei decimali.
Orwellophile,

Ciao! In realtà il codice è come l'ho scritto la prima volta in jsfiddle. Negli ultimi anni ho imparato a usare la stenografia e bit a bit. Dispositivi mobili lenti, Internet lenta, poco spazio ... facendo così ho risparmiato molto tempo. Ma non è tutto, le prestazioni complessive sono aumentate drasticamente in ogni browser e l'intero codice si carica molto più velocemente ... Non uso jquery, quindi non devo caricare 100kb ogni volta. Devo anche dire che scrivo javascript anche in microcontrollori, Smart TV, console di gioco. quelli hanno spazio limitato (MCU), prestazioni (SmartTV) e connessioni a volte lente (Mobile)
cocco

Ho detto che spero che tu capisca la mia scelta. Tutto quello che posso fare è spiegare ciò che non capisci o dall'altra parte sono sempre felice di imparare cose nuove. Se c'è qualcosa nel mio codice che potrebbe aumentare le prestazioni o risparmiare spazio, sono felice di sentirlo.
cocco,

18
La minimizzazione dovrebbe far parte del processo di creazione, non del tuo stile di codifica. Nessuno sviluppatore serio utilizzerà questo codice a causa di ciò poiché impiega troppo tempo a leggere e verificare la correttezza.
huysentruitw,

1
Per coloro che odiano vedere "15.00 Bytes", puoi semplicemente modificare un po 'questa parte:.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"


2
Se si voleva sbarazzarsi dello spazio extra per byte, è possibile utilizzare lo spazio di larghezza pari a zero \u200b: '\u200bKMGTP'.
cdmckay,

15

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

AGGIORNAMENTO Per coloro che usano es6 ecco una versione senza stato di questo stesso 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,
};

1
Grazie mille. Hai appena dimenticato "byte" all'interno di Math.log () nella prima riga della funzione getBytes
BaptWaels,

Molto bella. Per chiarimento delle ambiguità e con la notazione ES6, è possibile utilizzare questo: return (! Bytes && '0 Bytes') || ${(bytes / (1024 ** i)).toFixed(2)} ${suffixes[i]};
Little Brain,

12

Basato sull'idea di cocco , ecco un esempio meno compatto, ma si spera più 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> 

8

Volevo il comportamento del "file manager" (ad esempio, Esplora risorse) in cui il numero di posizioni decimali è proporzionale alla dimensione del numero. Apparentemente nessuna delle altre risposte lo fa.

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

Ecco alcuni esempi:

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"

usando toFixed lo converte in una stringa, quindi il tuo giro è una stringa o un numero. questa è una cattiva pratica, puoi facilmente riconvertirla in un numero:+num.tofixed(2)
Vincent Duprez,

Non .toPrecision(3)copre tutti questi casi? Oh .. immagino che non copra tra 1000 e 1023. Bummer.
mpen

7

Un altro esempio simile a quelli qui

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

Misura prestazioni trascurabilmente migliori rispetto alle altre con caratteristiche simili.


Ciò fornisce prestazioni migliori rispetto ad altre risposte. Sto usando questo. Alcuni altri hanno bloccato le mie schede di Chrome e preso CPU del 99,9% mentre stavo facendo un calcolo periodico.
Nir Lanka,

5

Ecco il mio - funziona anche con file molto grandi -_-

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

Combina il successo prestazionale di entrambi i loop e l'uso di esponenziazione, pur essendo piuttosto difficile da leggere. Non vedo davvero il punto.
extra il

2
Non usarlo allora. È solo la CPU del client che viene utilizzata, quindi chi se ne frega;)
fiffy

2
@fiffy Bene, anche la CPU client è preziosa, specialmente su dispositivi mobili e con applicazioni complesse. :)
Raito,

5

Basato sulla risposta di cocco ma leggermente desugerificato (onestamente, quelli con cui mi sentivo a mio agio sono rimasti / aggiunti) e non mostra zeri finali ma supporta ancora 0, spero di essere utile per gli altri:

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


4
1551859712 / 1024 = 1515488
1515488 / 1024 = 1479.96875
1479.96875 / 1024 = 1.44528198242188

La tua soluzione è corretta La cosa importante da capire è che, al fine di ottenere da 1551859712a 1.5, quello che dovete fare divisioni dal 1000, ma i byte vengono contati in pezzi binario a decimale di 1024, quindi, perché il valore Gigabyte è inferiore.


@Eli ... sì, sembra così. Immagino che mi aspettassi "1.5" dal suo 1551859712, ma ciò significherebbe che sono in decimale e non binario.
Hristo,

3

Ho trovato interessante la risposta di @ cocco , ma ho riscontrato i seguenti problemi:

  1. Non modificare tipi nativi o tipi che non possiedi
  2. Scrivi codice pulito e leggibile per gli esseri umani, lascia che i minimizzatori ottimizzino il codice per le macchine
  3. (Bonus per utenti TypeScript) Non funziona bene con TypeScript

Dattiloscritto:

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

1

Questo è il miglioramento dimensionale della risposta aperta

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


0

Per coloro che usano Angular, c'è un pacchetto chiamato angular-pipesche ha una pipe per questo:

File

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

Link ai documenti .


0

La mia risposta potrebbe essere in ritardo, ma credo che aiuterà qualcuno.

Prefisso metrico:

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

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

Esempi:

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

-1

lascia byte = 1024 * 10 * 10 * 10;

console.log (getReadableFileSizeString (bytes))

restituirà 1000,0 kB invece di 1 MB

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.