Prawidłowy sposób konwersji rozmiaru w bajtach na KB, MB, GB w JavaScript


258

Mam ten kod do ukrytego rozmiaru w bajtach za pośrednictwem PHP.

Teraz chcę przekonwertować te rozmiary na rozmiary czytelne dla ludzi za pomocą JavaScript. Próbowałem przekonwertować ten kod na JavaScript, który wygląda następująco:

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

Czy to właściwy sposób na zrobienie tego? Czy istnieje prostszy sposób?


5
To faktycznie konwertuje do GiB, MiB i KiB. Jest to standardowe w przypadku rozmiarów plików, ale nie zawsze w przypadku rozmiarów urządzeń.
David Schwartz

Odpowiedzi:


761

Z tego: ( źródło )

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

Uwaga: jest to oryginalny kod, proszę użyć poprawionej wersji poniżej. Aliceljm nie aktywuje już skopiowanego kodu


Teraz, Naprawiono wersję nieuprawnioną i ES6'ed: (przez społeczność)

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

Teraz, Naprawiona wersja: (przez społeczność Stackoverflow, + Minified przez JSCompress )

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

Stosowanie :

// formatBytes(bytes,decimals)

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

Demo / źródło:

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: Zmień k = 1000lub sizes = ["..."]jak chcesz ( bity lub bajty )


8
(1) dlaczego bajty = 0 to „nie dotyczy”? Czy to nie tylko „0 B”? (2) Math.round nie ma parametru dokładności. Lepiej skorzystam(bytes / Math.pow(1024, i)).toPrecision(3)
rozproszony

4
toFixed(n)jest prawdopodobnie bardziej odpowiednie niż toPrecision(n)mieć stałą precyzję dla wszystkich wartości. Aby uniknąć końcowych zer (np .:) bytesToSize(1000) // return "1.00 KB", moglibyśmy użyć parseFloat(x). Proponuję zastąpić ostatnią linię przez: return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];. Po poprzedniej zmianie wyniki są następujące: 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,

3
Wierzę, że liczba mnoga jest używana dla 0: „0 Bajtów”
nima

14
Powiedziałbym, że minify jest fajny, ale w odpowiedzi na zamianę stosu lepiej jest mieć bardziej szczegółowy i czytelny kod.
donquixote

2
KB = bajty Kelvina w jednostkach SI. co jest nonsensowne. Powinien to być KB.
Brennan T

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

34
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;
  }
  //include a decimal point and a tenths-place digit if presenting 
  //less than ten of KB or greater units
  return(n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
}

Wyniki:

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

15

Możesz użyć biblioteki filesizejs .


Wydaje mi się, że ta biblioteka zapewnia dokładną reprezentację, ponieważ 1024 bajty to 1 KB, a nie 1000 bajtów (jak zapewniają niektóre inne rozwiązania tutaj). Dzięki @maurocchi
WM

3
@WM to stwierdzenie nie jest prawdziwe. 1kB = 1000 bajtów. W kibibajcie jest 1024 bajty. W przeszłości było zamieszanie, więc te dwa terminy dokładnie wyjaśniają różnicę wielkości.
Brennan T

2
@BrennanT To zależy, ile masz lat. 1KB miał 1024 bajty i większość ludzi w pewnym wieku nadal postrzega go jako taki.
kojow7

14

Istnieją 2 rzeczywiste sposoby przedstawiania rozmiarów w odniesieniu do bajtów, są to jednostki SI (10 ^ 3) lub IEC (2 ^ 10). Istnieje również JEDEC, ale ich metoda jest dwuznaczna i myląca. Zauważyłem, że w innych przykładach występują błędy, takie jak użycie KB zamiast kB do reprezentowania kilobajta, więc postanowiłem napisać funkcję, która rozwiąże każdy z tych przypadków przy użyciu zakresu obecnie akceptowanych jednostek miary.

Na końcu znajduje się bit formatujący, który sprawi, że liczba będzie wyglądać nieco lepiej (przynajmniej dla mojego oka), możesz usunąć to formatowanie, jeśli nie pasuje do twojego celu.

Cieszyć się.

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

13

Oto jeden liniowiec:

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

Lub nawet:

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


Fajnie! Ale jeśli 1k to 1024, a nie 1000?
l2aelba

2
Wyliczenie to jest traktowanie 1k jako 2 ^ 10, 1M jak 2 ^ 20 i tak dalej. jeśli chcesz, aby 1k wynosił 1000, możesz go nieco zmienić, aby użyć log10.
iDaN5x

1
Oto wersja, która traktuje 1K jako 1000:val => 'BKMGT'[~~(Math.log10(val)/3)]
iDaN5x

1
To jest miłe! Rozszerzyłem to, aby zwrócić pełny ciąg, który chciałem z mojej funkcji:i = ~~(Math.log2(b)/10); return (b/Math.pow(1024,i)).toFixed(2) + ("KMGTPEZY"[i-1]||"") + "B"
v0rtex

4

Lepszym rozwiązaniem byłoby użycie operacji bitowej. Spróbuj tego

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

1
Uzyskaj pozostałe bajty. To zapewni część dziesiętną.
Buzz LIghtyear

1
Jego 1024. Jeśli potrzebujesz 100, przesuń odpowiednio bity.
Buzz LIghtyear


3
Nie pobieraj kodu z Internetu bez jego zrozumienia lub przynajmniej przetestowania. To dobry przykład kodu, który jest po prostu zły. Spróbuj uruchomić, przekazując 3 (zwraca „1Bajt”) lub 400000.
Amir Haghighat

10
Drogi Amir Haghighat, jest to podstawowy kod napisany wyłącznie przeze mnie. W javasript po 32 bitach wartości całkowitej kod nie będzie działał, ponieważ liczba całkowita to tylko cztery bajty. Są to podstawowe informacje programistyczne, które powinieneś znać. Stackoverflow służy wyłącznie do prowadzenia ludzi, a nie karmienia łyżką.
Buzz LIghtyear

4

Zgodnie z odpowiedzią Aliceljma usunąłem 0 po przecinku:

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

2

Pierwotnie użyłem odpowiedzi @Aliceljm dla projektu przesyłania pliku, nad którym pracowałem, ale ostatnio natknąłem się na problem polegający na tym, że plik był 0.98kbodczytywany jako 1.02mb. Oto zaktualizowany kod, którego teraz używam.

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

Powyższe zostanie następnie wywołane po dodaniu takiego pliku

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

To prawda, że ​​Windows odczytuje plik jako istniejący, 24.8mbale jestem w porządku z dodatkową precyzją.


2

To rozwiązanie opiera się na poprzednich rozwiązaniach, ale uwzględnia zarówno jednostki metryczne, jak i binarne:

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

Przykłady:

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

2

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


2

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

Aktualizuję odpowiedź @Aliceljm tutaj. Ponieważ miejsce dziesiętne ma znaczenie dla liczb 1,2-cyfrowych, zaokrąglam pierwsze miejsce po przecinku i zachowuję pierwsze miejsce po przecinku. Dla liczby 3-cyfrowej zaokrąglam miejsce jednostek i ignoruję wszystkie miejsca dziesiętne.

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

0

Oto jak bajt powinien być pokazany człowiekowi:

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

0

Chciałem tylko podzielić się swoimi opiniami. Miałem ten problem, więc moje rozwiązanie jest takie. Spowoduje to konwersję niższych jednostek na wyższe jednostki i odwrotnie, wystarczy podać argument toUnitifromUnit

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

Stąd pomysł


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

Ta funkcja jest łatwa do zrozumienia i naśladowania - możesz ją wdrożyć w dowolnym języku. Jest to powtarzający się podział wartości bajtów, aż do osiągnięcia poziomu bajtu (jednostki), który jest większy niż 1kb
Kjut

Krótka uwaga, istnieją różnice między przedrostkami binarnymi. Niektóre przestrzegają zasady SI base 10, a niektóre przestrzegają zasady base 2. Możesz przeczytać więcej tutaj . Jeśli jednak uważasz, że k to 1024, zamiast podziału, możesz po prostu użyć operatora shift, takiego jak byteVal >> 10. Również byłoby lepiej wykorzystać Math.trunc()do oddania liczb rzeczywistych do liczb całkowitych zamiast ofdivision o 1.
Cunning

Nie wysyłaj tylko kodu jako odpowiedzi, ale także wyjaśnij, co robi Twój kod i jak rozwiązuje problem pytania. Odpowiedzi z wyjaśnieniem są zazwyczaj wyższej jakości i częściej przyciągają entuzjastów.
Mark Rotteveel,

-7

Wypróbuj to proste obejście.

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");
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.