Converting file size in bytes to human-readable st

2020-01-26 13:07发布

I'm using this function to convert a file size in bytes to a human-readable file size:

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

However, it seems like this isn't 100% accurate. For example:

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

Shouldn't this be "1.5 GB"? It seems like the division by 1024 is losing precision. Am I totally misunderstanding something or is there a better way to do this?

15条回答
等我变得足够好
2楼-- · 2020-01-26 13:27

It depends on whether you want to use the binary or decimal convention.

RAM, for instance, is always measured in binary, so to express 1551859712 as ~1.4GiB would be correct.

On the other hand, hard disk manufacturers like to use decimal, so they would call it ~1.6GB.

And just to be confusing, floppy disks use a mixture of the two systems - their 1MB is actually 1024000 bytes.

查看更多
劳资没心,怎么记你
3楼-- · 2020-01-26 13:27

Here is a prototype to convert a number to a readable string respecting the new international standards.

There are two ways to represent big numbers: You could either display them in multiples of 1000 = 10 3 (base 10) or 1024 = 2 10 (base 2). If you divide by 1000, you probably use the SI prefix names, if you divide by 1024, you probably use the IEC prefix names. The problem starts with dividing by 1024. Many applications use the SI prefix names for it and some use the IEC prefix names. The current situation is a mess. If you see SI prefix names you do not know whether the number is divided by 1000 or 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});

This function contains no loop, and so it's probably faster than some other functions.

Usage:

IEC prefix

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

SI prefix

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

i set the IEC as default because i always used binary mode to calculate the size of a file... using the power of 1024


If you just want one of them in a short oneliner function:

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

if you have some questions about the functions just ask

查看更多
姐就是有狂的资本
4楼-- · 2020-01-26 13:29

Here's mine - works for really big files too -_-

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;
}
查看更多
5楼-- · 2020-01-26 13:29
1551859712 / 1024 = 1515488
1515488 / 1024 = 1479.96875
1479.96875 / 1024 = 1.44528198242188

Your solution is correct. The important thing to realize is that in order to get from 1551859712 to 1.5, you have to do divisions by 1000, but bytes are counted in binary-to-decimal chunks of 1024, hence why the Gigabyte value is less.

查看更多
Root(大扎)
6楼-- · 2020-01-26 13:29

For those who use Angular, there's a package called angular-pipes that has a pipe for this:

File

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

Link to the docs.

查看更多
姐就是有狂的资本
7楼-- · 2020-01-26 13:31

I wanted the "file manager" behavior (e.g., Windows Explorer) where the number of decimal places is proportional to the number size. Seemingly none of the other answers does this.

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

Here's some examples:

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"
查看更多
登录 后发表回答