Conversion de la taille du fichier en octets en une chaîne lisible par l'homme

239

J'utilise cette fonction pour convertir une taille de fichier en octets en une taille de fichier lisible par l'homme:

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

Cependant, il semble que ce ne soit pas précis à 100%. Par exemple:

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

Cela ne devrait-il pas être "1.5 GB"? Il semble que la division par 1024 perd de sa précision. Suis-je totalement incompris quelque chose ou existe-t-il une meilleure façon de le faire?

Hristo
la source
3
getReadableFileSizeString (0); renvoie 0,1 Ko; p
Daniel Magnusson
2
Pourquoi devrait-il être 1,5? C'est ce 1.445281982421875qui arrondit correctement à 1,4.
mpen
1
1551859712 / (1024 ^ 3) = 1,445281982421875 qui est correct!
HM
2
J'adore que tu aies ajouté YB. Personne douteux ne recevra même 1 YB pour sa DB. Cela coûtera 100 billions de dollars !
guyarad
4
@guyarad - il y a une image célèbre d'un disque dur de 5 Mo d'il y a 50 ans (était à la taille d'une pièce et pesait environ une tonne). Je suis sûr qu'à l'époque, ils ne
rêvaient

Réponses:

45

Cela dépend si vous souhaitez utiliser la convention binaire ou décimale.

La RAM, par exemple, est toujours mesurée en binaire, donc exprimer 1551859712 comme ~ 1,4 Go serait correct.

D'autre part, les fabricants de disques durs aiment utiliser la décimale, ils l'appelleraient donc ~ 1,6 Go.

Et juste pour être déroutant, les disquettes utilisent un mélange des deux systèmes - leur 1 Mo est en fait de 1024 000 octets.

Neil
la source
3
souper drôle ;-) "juste pour être déroutant, les disquettes utilisent un mélange des deux systèmes - leur 1 Mo est en fait 1024000 octets."
FranXho
vrai, les tailles de RAM sont mesurées en utilisant des unités CEI, les tailles de disques en utilisant la métrique .. il y a un module npm isomorphe pour convertir les deux: taille d'octet
Lloyd
351

En voici un que j'ai écrit:

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
la source
1
Je fais un ajustement: lors de l'évaluation du seuil, prenez la valeur absolue. De cette façon, la fonction prendra en charge les valeurs négatives. Belle fonction! Merci de ne pas utiliser une instruction switch !!
Aaron Blenkush
20
@AaronBlenkush: Quand auriez-vous une taille de fichier négative?
mpen
14
Je viens de copier votre fonction dans une feuille Google que j'utilise pour afficher la taille delta après une opération de "nettoyage". Avant, Après et Diff. L'opération de nettoyage a entraîné la croissance de certaines tables de base de données et la réduction d'autres. Par exemple, le tableau A a un diff de -1,95 Mo, tandis que le tableau B a un diff de 500 kB. Par conséquent: positif et négatif :-)
Aaron Blenkush
Voici la version compressée du 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: Merci pour la version réduite. Pouvez-vous me dire, cependant, pourquoi vous avez inséré les deux caractères Unicode invisibles U + 200C (ZERO WIDTH NON-JOINER) et U + 200B (ZERO WIDTH SPACE) après le E oft EiB ? Est-ce destiné à être un filigrane, afin que vous puissiez suivre qui a utilisé ce code? Si oui, je pense que vous auriez dû rendre cela transparent dans votre message.
Léviathan
81

Un autre mode de réalisation du calcul

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.
la source
8
semble ne pas gérer 0
Offirmo
4
Il gère ou ne gère pas 0? Après tout, cela avec un if (taille == 0) {} else {} est toujours plus élégant que la plupart que j'ai vu.
Rodrigo
13
Changer la première ligne var i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) );semble faire l'affaire si c'est 0. Elle renverra "0 B".
Gavin
Juste FYI; Je sais que la réponse est du JavaScript simple, mais si quelqu'un ne veut pas l'utiliser en TypeScript, cela ne fonctionne pas (pas tapé correctement, comme vous le faites toFixed, puis faites des mathématiques avec une chaîne. Que fait- * 1il?
Frexuz
1
Le *1change le type de données de chaîne en nombre, donc pour la valeur que 1024vous obtenez 1 kBau lieu de 1.00 kB. Vous pouvez rendre TypeScript heureux en faisant Number((size / Math.pow(1024, i)).toFixed(2))la même chose.
Adrian T
38

Voici un prototype pour convertir un nombre en une chaîne lisible en respectant les nouvelles normes internationales.

Il existe deux façons de représenter les grands nombres: vous pouvez soit les afficher en multiples de 1000 = 10 3 (base 10) ou 1024 = 2 10 (base 2). Si vous divisez par 1000, vous utilisez probablement les noms de préfixe SI, si vous divisez par 1024, vous utilisez probablement les noms de préfixe CEI. Le problème commence par la division par 1024. De nombreuses applications utilisent les noms de préfixe SI pour cela et certaines utilisent les noms de préfixe CEI. La situation actuelle est un gâchis. Si vous voyez des noms de préfixe SI, vous ne savez pas si le nombre est divisé par 1000 ou 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});

Cette fonction ne contient pas loopet est donc probablement plus rapide que certaines autres fonctions.

Usage:

Préfixe CEI

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

Préfixe SI

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

J'ai défini l'IEC par défaut car j'ai toujours utilisé le mode binaire pour calculer la taille d'un fichier ... en utilisant la puissance de 1024


Si vous voulez juste l'un d'eux dans une fonction oneliner courte:

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

si vous avez des questions sur les fonctions, demandez

cocco
la source
très beau code compact, j'ajouterais personnellement quelques caractères supplémentaires pour le contrôle des décimales.
Orwellophile
Salut! En fait, le code est la façon dont je l'ai écrit la première fois dans jsfiddle. Au cours des dernières années, j'ai appris à utiliser la sténographie et le bit à bit. Appareils mobiles lents, Internet lent, pas beaucoup d'espace ... ce faisant, j'ai économisé beaucoup de temps. Mais ce n'est pas tout, la performance globale a considérablement augmenté dans tous les navigateurs et le code entier se charge beaucoup plus rapidement ... je n'utilise pas jquery donc je n'ai pas à charger 100 Ko à chaque fois. Je dois également dire que j'écris javascript également dans les microcontrôleurs, les Smart TV, les consoles de jeux. ceux-ci ont un espace limité (MCU), des performances (SmartTV) et naturellement une connexion parfois lente (Mobile)
cocco
Dit que j'espère que vous comprenez mon choix. Tout ce que je peux faire, c'est expliquer ce que vous ne comprenez pas ou de l'autre côté, je suis toujours heureux d'apprendre de nouvelles choses. S'il y a quelque chose dans mon code qui pourrait augmenter les performances ou économiser de l'espace, je suis heureux de l'entendre.
cocco
18
La minification doit faire partie de votre processus de construction, pas de votre style de codage. Aucun développeur sérieux n'utilisera ce code à cause de cela car il prend trop de temps pour lire et vérifier l'exactitude.
huysentruitw
1
Pour ceux qui détestent voir "15,00 octets", vous pouvez juste modifier un peu cette partie:.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 Go"

sizeOf (7054110);
// => "6,73 Mo"

sizeOf ((3 * 1024 * 1024));
// => "3,00 Mo"

Joshaven Potter
la source
2
Si vous voulez vous débarrasser de l'espace supplémentaire pour les octets, vous pouvez utiliser l'espace de largeur zéro \u200b: '\u200bKMGTP'.
cdmckay
15

Solution en tant que composant 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>
        );
    }
});

MISE À JOUR Pour ceux qui utilisent es6, voici une version sans état de ce même composant

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
la source
1
Grand merci. Vous venez d'oublier les "octets" dans Math.log () dans la première ligne de la fonction getBytes
BaptWaels
Très agréable. Pour la désambiguïsation, et avec la notation ES6, vous pouvez utiliser ceci: return (! Bytes && '0 Bytes') || ${(bytes / (1024 ** i)).toFixed(2)} ${suffixes[i]};
Little Brain
12

Sur la base de l'idée de cocco , voici un exemple moins compact, mais j'espère plus complet.

<!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> 
KitKat
la source
8

Je voulais le comportement du "gestionnaire de fichiers" (par exemple, l'Explorateur Windows) où le nombre de décimales est proportionnel à la taille du nombre. Apparemment, aucune des autres réponses ne fait cela.

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

Voici quelques exemples:

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
la source
l'utilisation de toFixed le convertit en chaîne, donc votre tour est soit une chaîne, soit un nombre. c'est une mauvaise pratique, vous pouvez facilement le reconvertir en un nombre:+num.tofixed(2)
Vincent Duprez
Ne .toPrecision(3)couvre pas tous ces cas? Oh .. Je suppose que cela ne couvre pas entre 1000 et 1023. Bummer.
mpen
7

Un autre exemple similaire à ceux ici

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

Il mesure des performances sensiblement meilleures que les autres avec des fonctionnalités similaires.

Nick Kuznia
la source
Cela offre de meilleures performances que certaines autres réponses. J'utilise ça. Certains autres ont fait bloquer mes onglets Chrome et prendre 99,9% de CPU pendant que je faisais un calcul périodique.
Nir Lanka
5

Voici le mien - fonctionne aussi pour les très gros fichiers -_-

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
la source
Il combine les performances de la boucle et l'utilisation de l'exponentiation, tout en étant assez difficile à lire. Je ne vois pas vraiment le point.
spectras
2
Ne l'utilisez pas alors. C'est juste un processeur côté client qui est utilisé alors peu importe;)
fiffy
2
@fiffy Eh bien, le CPU client est également précieux, en particulier sur mobile et avec des applications complexes. :)
Raito
5

Basé sur la réponse de Cocco mais légèrement désugué (honnêtement, ceux avec lesquels j'étais à l'aise sont restés / ajoutés) et n'affiche pas de zéros à la fin mais prend toujours en charge 0, espérons être utile pour les autres:

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
la source
4
1551859712 / 1024 = 1515488
1515488 / 1024 = 1479.96875
1479.96875 / 1024 = 1.44528198242188

Votre solution est correcte. La chose importante à réaliser est que pour passer de 1551859712à 1.5, vous devez faire des divisions par 1000, mais les octets sont comptés en morceaux binaires à décimaux de 1024, d'où la valeur de Gigabyte inférieure.

Eli
la source
@Eli ... oui, on dirait bien. Je suppose que je m'attendais à "1,5" depuis son 1551859712, mais cela signifierait que je suis en décimal et non binaire.
Hristo
3

J'ai trouvé la réponse de @ cocco intéressante, mais j'ai eu les problèmes suivants:

  1. Ne modifiez pas les types natifs ou les types que vous ne possédez pas
  2. Écrivez du code propre et lisible pour les humains, laissez les minificateurs optimiser le code pour les machines
  3. (Bonus pour les utilisateurs de TypeScript) Ne fonctionne pas bien avec TypeScript

Manuscrit:

 /**
 * 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
la source
1

Ceci est l'amélioration de la taille de la réponse 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
la source
0

Pour ceux qui utilisent Angular, il y a un paquet appelé angular-pipesqui a un tuyau pour cela:

Fichier

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

Lien vers les documents .

Sinandro
la source
0

Ma réponse pourrait être en retard, mais je suppose que cela aidera quelqu'un.

Préfixe métrique:

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

Préfixe binaire:

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

Exemples:

// 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
la source
-1

laissez octets = 1024 * 10 * 10 * 10;

console.log (getReadableFileSizeString (octets))

renverra 1000.0Кб au lieu de 1MB

webolizzer
la source