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

IT技术 javascript filesize human-readable
2021-02-22 22:22:37

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

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 正在失去精度。我是完全误解了什么还是有更好的方法来做到这一点?

6个回答

这是我写的一篇:

/**
 * Format bytes as human-readable text.
 * 
 * @param bytes Number of bytes.
 * @param si True to use metric (SI) units, aka powers of 1000. False to use 
 *           binary (IEC), aka powers of 1024.
 * @param dp Number of decimal places to display.
 * 
 * @return Formatted string.
 */
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(1551859712))  // 1.4 GiB
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

我正在做一个调整:在评估阈值时,取绝对值。这样,该函数将支持负值。不错的功能!感谢您没有使用 switch 语句!!
2021-04-17 22:22:37
@兰德斯00:感谢缩小版本。你能告诉我,虽然,你为什么在之后插入两个无形的Unicode字符U + 200C(零宽无连接符)和U + 200B(零宽度的空格)è经常EIB这是否打算作为水印,以便您可以跟踪谁使用了此代码?如果是这样,我认为你应该在你的帖子中做到这一点。
2021-04-25 22:22:37
@AaronBlenkush:你什么时候会有负文件大小?
2021-04-28 22:22:37
我刚刚将您的函数复制到我用来显示“清理”操作后的大小增量的 Google 表格中。之前、之后和差异。清理操作导致一些数据库表增长,而其他数据库表减少。例如,表 A 的差异为 -1.95 MB,而表 B 的差异为 500 kB。因此:正面和负面:-)
2021-04-30 22:22:37
这是脚本的压缩版本: 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]}
2021-05-12 22:22:37

计算的另一个实施例

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];
};
*1数据类型从字符串更改为数字,因此对于1024您获得的值1 kB而不是1.00 kB. 你可以通过做Number((size / Math.pow(1024, i)).toFixed(2))同样的事情来让 TypeScript 开心
2021-04-19 22:22:37
似乎不处理 0
2021-05-05 22:22:37
它处理或不处理 0?毕竟,这个带有 if(size == 0) {} else {} 仍然比我见过的大多数更优雅。
2021-05-15 22:22:37
将第一行更改为 0var i = size == 0 ? 0 : Math.floor( Math.log(size) / Math.log(1024) );似乎可以解决问题。它将返回“0 B”。
2021-05-16 22:22:37
仅供参考;我知道答案是普通的 JavaScript,但是如果有人不想在 TypeScript 中使用它,它就不起作用(输入不正确,就像您正在做的那样toFixed,然后用字符串进行数学运算。这是* 1做什么的?
2021-05-16 22:22:37

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

例如,RAM 总是以二进制表示,因此将 1551859712 表示为 ~1.4GiB 是正确的。

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

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

是的,RAM 大小使用 IEC 单位测量,磁盘大小使用公制......有一个同构 npm module可以转换两者:字节大小
2021-04-18 22:22:37
晚饭好笑;-) “只是让人困惑,软盘混合使用这两种系统——它们的 1MB 实际上是 1024000 字节。”
2021-05-01 22:22:37

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

有两种表示大数字的方法:您可以以 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});

此函数包含 no 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));

如果您对功能有一些疑问,请询问

缩小应该是您构建过程的一部分,而不是您的编码风格。由于阅读和验证正确性需要很长时间,因此没有认真的开发人员会使用此代码。
2021-04-16 22:22:37
说希望你理解我的选择。我所能做的就是解释你不理解的东西,或者在另一方面,我总是乐于学习新事物。如果我的代码中有一些东西可以提高性能或节省空间,我很高兴听到它。
2021-04-19 22:22:37
你好!实际上,代码是我第一次在 jsfiddle 中编写的。在过去的几年里,我学会了使用速记和按位。移动设备速度慢,互联网速度慢,空间不大......这样做我节省了很多时间。但这还不是全部,每个浏览器的整体性能显着提高,整个代码加载速度更快……我不使用 jquery,所以我不必每次都加载 100kb。我还需要说我也在微控制器、智能电视、游戏机中编写 javascript。那些空间有限(MCU)、性能(SmartTV)和自然有时连接缓慢(移动)
2021-04-28 22:22:37
对于那些讨厌看到“15.00 Bytes”的人,你可以稍微修改一下这部分: .toFixed(e ? 2 : 0)
2021-05-09 22:22:37
非常好的紧凑代码,不过我个人会添加几个额外的字符来控制小数位。
2021-05-12 22:22:37
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';
}

大小(2054110009);
//=> "1.91 GB"

大小(7054110);
//=> "6.73 MB"

sizeOf((3*1024*1024));
//=> "3.00 MB"

如果您想摆脱字节的额外空间,您可以使用零宽度空间\u200b: '\u200bKMGTP'
2021-05-07 22:22:37