Преобразование размера файла в байтах в удобочитаемую строку

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
Даниэль Магнуссон
2
Почему это должно быть 1,5? Это то, 1.445281982421875что правильно округляется до 1,4.
mpen
1
1551859712 / (1024 ^ 3) = 1.445281982421875, что правильно!
HM
2
Мне нравится, что вы добавили YB. Сомнительно, что кто-нибудь получит хотя бы 1 YB за свою БД. Это будет стоить 100 триллионов долларов !
Гайарад
4
@guyarad - есть знаменитая фотография 5-мегабайтного жесткого диска 50-летней давности (размером с комнату и весом около тонны). я уверен, что тогда они даже не мечтали о ГБ и ТБ, и посмотрите, где мы находимся сегодня ... никогда не говори никогда ;-)
TheCuBeMan

Ответы:

45

Это зависит от того, хотите ли вы использовать двоичное или десятичное соглашение.

ОЗУ, например, всегда измеряется в двоичном формате, поэтому было бы правильно выразить 1551859712 как ~ 1,4 ГБ.

С другой стороны, производители жестких дисков предпочитают использовать десятичную дробь, поэтому они назвали бы ее ~ 1,6 ГБ.

И просто чтобы запутать, дискеты используют смесь двух систем - их 1 МБ на самом деле 1024000 байт.

Нил
источник
3
Ужин забавный ;-) «просто чтобы сбить с толку, дискеты используют смесь двух систем - их 1 МБ на самом деле 1024000 байт».
FranXho
правда, размеры ОЗУ измеряются в единицах МЭК, а размеры дисков - в метрических единицах. Изоморфный модуль npm для преобразования обоих: размер в байтах
Lloyd
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

mpen
источник
1
Я делаю одну корректировку: при оценке порога принимайте абсолютное значение. Таким образом, функция будет поддерживать отрицательные значения. Отличная функция! Спасибо, что не используете оператор switch !!
Аарон Бленкуш
20
@AaronBlenkush: Когда у вас будет отрицательный размер файла?
mpen
14
Я только что скопировал вашу функцию в Google Sheet, который я использую для отображения разницы в размере после операции «очистки». До, После и Дифф. Операция очистки привела к росту некоторых таблиц базы данных и сокращению других. Например, таблица A имеет разность -1,95 МБ, а таблица B имеет разность 500 кБ. Поэтому: положительный и отрицательный :-)
Аарон Бленкуш
Вот сжатая версия скрипта: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: Спасибо за уменьшенную версию. Можете ли вы сказать мне, почему вы вставили два невидимых символа Unicode U + 200C (ZERO WIDTH NON-JOINER) и U + 200B (ZERO WIDTH SPACE) после E oft EiB ? Это должен быть водяной знак, чтобы вы могли отслеживать, кто использовал этот код? Если так, я думаю, вы должны были сделать это прозрачным в своем посте.
Левиафан
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
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. Вы можете сделать TypeScript счастливым, Number((size / Math.pow(1024, i)).toFixed(2))выполнив то же самое.
Адриан Т
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});

Эта функция не содержит no loop, и поэтому она, вероятно, быстрее, чем некоторые другие функции.

Использование:

Префикс IEC

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

Префикс СИ

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

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

Использование:

console.log(fileSizeIEC(7412834521));

если у вас есть вопросы о функциях, просто спросите

кокко
источник
очень хороший компактный код, я бы лично добавил пару дополнительных символов для контроля десятичных знаков.
Орвеллофил
Здравствуй! На самом деле код, как я написал его в первый раз в jsfiddle. В последние годы я научился использовать сокращенное и побитовое. Медленные мобильные устройства, медленный интернет, не так много места ... делая это, я сэкономил много времени. Но это еще не все, общая производительность резко возросла в каждом браузере, и весь код загружается намного быстрее ... Я не использую jquery, поэтому мне не нужно загружать 100 КБ каждый раз. Я также должен сказать, что я пишу JavaScript также в микроконтроллерах, Smart TV, игровых приставках. у них ограниченное пространство (MCU), производительность (SmartTV) и, естественно, иногда медленное соединение (Mobile)
cocco
Сказал, что я надеюсь, что вы понимаете мой выбор. Все, что я могу сделать, это объяснить, что ты не понимаешь, или с другой стороны, я всегда рад изучать новые вещи. Если в моем коде есть что-то, что может повысить производительность или сэкономить место, я буду рад это услышать.
Cocco
18
Минификация должна быть частью вашего процесса сборки, а не вашего стиля кодирования. Ни один серьезный разработчик не будет использовать этот код из-за этого, так как чтение и проверка правильности занимает слишком много времени.
huysentruitw
1
Для тех, кто не .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 ГБ"

SizeOf (7054110);
// => "6,73 МБ"

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

Джошавен Поттер
источник
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
Большое спасибо. Вы просто забыли «байты» внутри Math.log () в первой строке функции
getBytes
Очень хорошо. Для устранения неоднозначности и с нотацией ES6 вы можете использовать это: return (! Bytes && '0 Bytes') || ${(bytes / (1024 ** i)).toFixed(2)} ${suffixes[i]};
Маленький мозг
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 Explorer), где количество десятичных разрядов пропорционально размеру числа. По-видимому, ни один из других ответов не делает этого.

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)
Винсент
Не .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%, как я делал периодические вычисления.
Нир Ланка,
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;
}
fiffy
источник
Он сочетает в себе снижение производительности как циклического, так и использования возведения в степень, при этом его довольно сложно читать. Я не вижу смысла.
спектры
2
Не используйте это тогда. Это просто клиентский процессор, который используется, так кого волнует;)
5
2
@fiffy Ну, клиентский ЦП тоже ценный, особенно на мобильных устройствах и со сложными приложениями. :)
Райто
5

Основанный на ответе кокко, но немного десегерифицированный (честно говоря, те, с которыми мне было удобно, остались / добавлены) и не показывает завершающие нули, но все еще поддерживает 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

Ваше решение правильное. Важно понимать, что для того, чтобы перейти от 1551859712к 1.5, вам нужно делить на 1000, но байты считаются в двоично-десятичных блоках 1024, поэтому значение гигабайта меньше.

Eli
источник
@ Или ... да, похоже. Я предполагаю, что ожидал «1,5» с момента его 1551859712, но это означало бы, что я в десятичной, а не в двоичной форме
Христо
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')
});
moribvndvs
источник
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 -->

Ссылка на документы .

Sinandro
источник
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
Kerkouch
источник
-1

пусть байты = 1024 * 10 * 10 * 10;

console.log (getReadableFileSizeString (байт))

вернет 1000.0Кб вместо 1МБ

webolizzer
источник