将文件大小(以字节为单位)转换为可读字符串


239

我正在使用此功能将以字节为单位的文件大小转换为人类可读的文件大小:

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

但是,这似乎不是100%准确的。例如:

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

这不是"1.5 GB"吗?似乎除以1024会失去精度。我是完全误解了某件事还是有更好的方法来做到这一点?


3
getReadableFileSizeString(0); 返回0.1kb; p
Daniel Magnusson

2
为什么要为1.5?这是1.445281982421875其正常向下取整为1.4。
mpen 2013年

1
1551859712 /(1024 ^ 3)= 1.445281982421875是正确的!
2014年

2
我爱你补充YB。令人怀疑的是,他的DB甚至将获得1 YB。它将花费100万亿美元
Guyarad

4
@guyarad-有一幅著名图片,描绘了50年前的5MB硬盘(当时只有一个房间,重约一吨)。我敢肯定,那时他们甚至都没有梦到GB和TB,看看我们今天的位置...永远不要说永不;-)
TheCuBeMan

Answers:


45

这取决于您要使用二进制还是十进制约定。

例如,RAM始终以二进制进行测量,因此将1551859712表示为〜1.4GiB是正确的。

另一方面,硬盘制造商喜欢使用十进制,因此他们将其称为〜1.6GB。

令人困惑的是,软盘使用了两种系统的混合-它们的1MB实际上是1024000字节。


3
超级有趣;-)“仅是令人困惑,软盘使用了两种系统的混合-它们的1MB实际上是1024000字节。”
FranXho

真的,RAM大小均采用IEC单位测量,大小盘采用公制单位..有一个同构NPM模块,这两个转换:字节大小
劳埃德

351

这是我写的:

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
我正在做一个调整:在评估阈值时,取绝对值。这样,函数将支持负值。功能不错!谢谢您不使用switch语句!!
亚伦·布伦库什

20
@AaronBlenkush:什么时候文件大小为负数?
mpen 2014年

14
我只是将您的函数复制到Google表格中,用于在执行“清理”操作后显示尺寸增量。之前,之后和差异。清理操作导致一些数据库表的增加,而另一些数据库表的减少。例如,表A的差异为-1.95 MB,而表B的差异为500 kB。因此:正负:-)
亚伦·布伦库什

这是脚本的压缩版本: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 2015年

1
@ RAnders00:感谢您的缩小版本。但是,您能告诉我为什么在E oft EiB之后插入两个不可见的Unicode字符U + 200C(零宽度非JOINER)和U + 200B(零宽度空间)吗?是否打算将其作为水印,以便您可以跟踪谁使用了此代码?如果是这样,我认为您应该在帖子中做到透明。
Leviathan

81

计算的另一个实施例

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
似乎无法处理0
Offirmo 2014年

4
是否处理0?毕竟,使用if(size == 0){} else {}仍然比我见过的大多数优雅。
罗德里戈

13
如果将第一行更改为var i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) );0,似乎可以解决问题。它将返回“ 0 B”。
加文

仅供参考;我知道答案是普通的JavaScript,但是如果有人不想在TypeScript中使用它,那么它将无法正常工作(在您进行输入toFixed然后再对字符串进行数学运算时,输入的类型不正确。该* 1怎么办?
Frexuz

1
*1数据类型从字符串更改为数字,因此对于1024您获得的值1 kB而不是1.00 kB。您可以通过Number((size / Math.pow(1024, i)).toFixed(2))完成同一件事来使TypeScript高兴。
阿德里安T

38

这是一个将数字转换为符合新国际标准的可读字符串的原型。

有两种表示大数的方法:您可以以1000 = 10 3(以10为底)或1024 = 2 10(以2为底)的倍数显示它们。如果除以1000,则可能使用SI前缀名称;如果除以1024,则可能使用IEC前缀名称。问题开始于除以1024。许多应用程序为此使用SI前缀名称,而某些应用程序使用IEC前缀名称。目前的情况一团糟。如果看到SI前缀名称,则不知道该数字是1000还是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});

该函数不包含loop,因此它可能比某些其他函数要快。

用法:

IEC前缀

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

SI前缀

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

我将IEC设置为默认值,因为我一直使用二进制模式来计算文件的大小...使用1024的幂


如果您只希望其中一个简短的oneliner函数:

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

国际电工委员会

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

用法:

console.log(fileSizeIEC(7412834521));

如果您对功能有任何疑问,请问


非常紧凑的代码,我个人会添加一些额外的字符来控制小数位。
Orwellophile 2015年

嗨!实际上,代码就是我在jsfiddle中第一次编写它的方式。在过去的几年中,我学会了自己使用速记和按位运算。缓慢的移动设备,缓慢的互联网,不多的空间...这样做节省了很多时间。但这还不是全部,每个浏览器的整体性能都大大提高了,整个代码的加载速度大大加快了...我不用jquery了,所以我不必每次都加载100kb。我还需要说,我也在微控制器,智能电视,游戏机中编写了javascript。那些空间有限(MCU),性能(SmartTV)和自然连接有时很慢(移动)
cocco 2015年

说,希望你能理解我的选择。我所能做的就是解释您不了解的内容,或者在另一方面,我总是乐于学习新事物。如果我的代码中有某些东西可以提高性能或节省空间,我很高兴听到它。
cocco

18
缩小应该是您构建过程的一部分,而不是您的编码风格。没有认真的开发人员会使用此代码,因为这样做会花费很长时间读取和验证正确性。
huysentruitw

1
对于那些讨厌看到“ 15.00字节”的人,您可以稍微修改一下这一部分:.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
如果您想摆脱多余的字节空间,可以使用零宽度的空间\u200b'\u200bKMGTP'
cdmckay,

15

解决方案作为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>
        );
    }
});

更新 对于使用es6的用户,这里是该组件的无状态版本

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
十分感谢。您只是在getBytes函数的第一行中忘记了Math.log()中的“字节”
BaptWaels

非常好。为了消除歧义,并使用ES6表示法,您可以使用以下方法:return(!bytes &&'0 Bytes')|| ${(bytes / (1024 ** i)).toFixed(2)} ${suffixes[i]};
Little Brain

12

基于cocco的想法,这是一个不太紧凑的示例,但希望更全面。

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

我想要“文件管理器”行为(例如Windows资源管理器),其中小数位数与数字大小成正比。似乎没有其他答案可以做到这一点。

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

以下是一些示例:

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"

使用toFixed会将其转换为字符串,因此您的回合可以是字符串或数字。这是不好的做法,您可以轻松地将其转换回数字:+num.tofixed(2)
Vincent Duprez,

.toPrecision(3)不能涵盖所有这些情况?哦..我想它不能涵盖1000到1023之间。
mpen

7

另一个类似于这里的例子

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

与具有类似功能的其他产品相比,它的性能可忽略不计。


与某些其他答案相比,这确实提供了更好的性能。我正在用这个。其他一些使我的Chrome标签页挂起,并在我进行定期计算时占用了99.9%的CPU。
尼尔斯里兰卡

5

这是我的-也适用于大型文件-_-

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

它结合了循环和指数运算的性能优势,同时很难阅读。我真的不明白这一点。
频谱

2
那就不要使用它。只是使用了客户端cpu,所以谁在乎;)
2015年

2
@fiffy好吧,客户端CPU也很宝贵,尤其是在移动设备和复杂应用程序上。:)
Raito

5

根据cocco的回答,但略微不加修饰(老实说,我感到满意的那些仍保留/增加),并且不显示尾随零但仍支持0,希望对其他人有用:

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

您的解决方案是正确的。要意识到的重要一点是,要从15518597121.5,必须除以1000,但是字节以1024的二进制到十进制的块计数,因此Gigabyte值较小。


@Eli ...是的,似乎是这样。我猜我期望自其1551859712起为“ 1.5”,但这意味着我使用的是十进制而不是二进制。
赫里斯特

3

我发现@cocco的答案很有趣,但是存在以下问题:

  1. 不要修改本机类型或您不拥有的类型
  2. 为人类编写清晰易读的代码,让采矿者优化机器代码
  3. (针对TypeScript用户的奖励)与TypeScript搭配使用效果不佳

打字稿:

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

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


0

对于那些使用的人Angular,有一个名为的软件包angular-pipes,其中包含一个用于此的管道:

文件

import { BytesPipe } from 'angular-pipes';

用法

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

链接到文档


0

我的答案可能太晚了,但我想它将对某人有所帮助。

指标前缀:

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

二进制前缀:

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

例子:

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

让字节= 1024 * 10 * 10 * 10;

console.log(getReadableFileSizeString(bytes))

将返回1000.0Кб而不是1MB

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.