Правильный способ преобразования размера в байтах в КБ, МБ, ГБ в JavaScript
Я получил этот код для преобразования размера в байтах с помощью PHP.
Теперь я хочу преобразовать эти размеры в удобочитаемые размеры с помощью JavaScript. Я попытался преобразовать этот код в JavaScript, который выглядит следующим образом:
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;
}
Это правильный способ сделать это? Есть ли более простой способ?
34 ответа
Из этого: ( источник)
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];
};
Примечание: это оригинальный код, пожалуйста, используйте фиксированную версию ниже. Aliceljm больше не активирует свой скопированный код
Теперь исправлена версия: (от сообщества Stackru, + Minified by JSCompress)
function formatBytes(a,b){if(0==a)return"0 Bytes";var c=1024,d=b||2,e=["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"],f=Math.floor(Math.log(a)/Math.log(c));return parseFloat((a/Math.pow(c,f)).toFixed(d))+" "+e[f]}
Использование:
// formatBytes(bytes,decimals)
formatBytes(1024); // 1 KB
formatBytes('1024'); // 1 KB
formatBytes(1234); // 1.21 KB
formatBytes(1234, 3); // 1.205 KB
Демо / источник:
function formatBytes(bytes,decimals) {
if(bytes == 0) return '0 Bytes';
var k = 1024,
dm = decimals <= 0 ? 0 : decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
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: изменить k = 1000
или же sizes = ["..."]
как вы хотите (биты или байты)
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;
return(n.toFixed(n >= 10 || l < 1 ? 0 : 1) + ' ' + units[l]);
}
Результаты:
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
function formatBytes(bytes) {
if(bytes < 1024) return bytes + " Bytes";
else if(bytes < 1048576) return(bytes / 1024).toFixed(3) + " KB";
else if(bytes < 1073741824) return(bytes / 1048576).toFixed(3) + " MB";
else return(bytes / 1073741824).toFixed(3) + " GB";
};
Вот один вкладыш:
val => ['Bytes','Kb','Mb','Gb','Tb'][Math.floor(Math.log2(val)/10)]
Или даже:
val => 'BKMGT'[~~(Math.log2(val)/10)]
Существует два реальных способа представления размеров, когда они связаны с байтами, это единицы СИ (10^3) или единицы МЭК (2^10). Есть также JEDEC, но их метод неоднозначен и запутан. Я заметил, что в других примерах есть ошибки, такие как использование KB вместо kB для представления килобайта, поэтому я решил написать функцию, которая будет решать каждый из этих случаев, используя диапазон принятых в настоящее время единиц измерения.
В конце есть бит форматирования, который заставит число выглядеть немного лучше (по крайней мере, на мой взгляд), не стесняйтесь удалить это форматирование, если оно не соответствует вашим целям.
Наслаждаться.
// 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];
}
}
Служебный метод для форматирования байтов в наиболее логическую величину (КБ, МБ или ГБ)
Просто немного изменил код ответа @zayarTun, чтобы включить дополнительный параметр, представляющий количество десятичных знаков в результате (также, если десятичные знаки равны нулю, тогда не нужно показывать результат, например 15,00 КБ, вместо этого достаточно 15 КБ, поэтому я обернул значение результата в
parseFloat()
)
bytesForHuman(bytes, decimals = 2) {
let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
let i = 0
for (i; bytes > 1024; i++) {
bytes /= 1024;
}
return parseFloat(bytes.toFixed(decimals)) + ' ' + units[i]
}
Использование побитовой операции было бы лучшим решением. Попробуй это
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 ;
}
У меня это работает.
bytesForHuman(bytes) {
let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
let i = 0
for (i; bytes > 1024; i++) {
bytes /= 1024;
}
return bytes.toFixed(1) + ' ' + units[i]
}
Речь не идет о преобразовании байтов в другие единицы измерения, но это помогает правильно отображать число и единицу измерения для текущей локали:
bytes.toLocaleString(undefined, {
style: 'unit',
unit: 'gigabyte',
})
Дополнительные параметры и подробности можно найти здесь: https://v8.dev/features/intl-numberformat#units
Согласно Aliceljm, я удалил 0 после десятичного числа:
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];
}
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));
Это решение основано на предыдущих решениях, но учитывает как метрические, так и двоичные единицы:
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];
}
Примеры:
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
Вот надежный и эффективный способ конвертировать байты. Единственное, что вам нужно, это установить библиотеку mathjs для точного расчета. Просто скопируйте и вставьте.
import { multiply, divide, round } from "mathjs";
class Size {
constructor(value, unit) {
this.value = value;
this.unit = unit.toUpperCase();
}
}
async function byteToSize(bytes) {
const B = 1;
const KB = multiply(B, 1024);
const MB = multiply(KB, 1024);
const GB = multiply(MB, 1024);
const TB = multiply(GB, 1024);
const PB = multiply(TB, 1024);
if (bytes <= KB) {
// @returns BYTE
const result = round(divide(bytes, B));
const unit = `B`;
return new Size(result, unit);
}
if (bytes <= MB) {
// @returns KILOBYTE
const result = round(divide(bytes, KB));
const unit = `KB`;
return new Size(result, unit);
}
if (bytes <= GB) {
// @returns MEGABYTE
const result = round(divide(bytes, MB));
const unit = `MB`;
return new Size(result, unit);
}
if (bytes <= TB) {
// @returns GIGABYTE
const result = round(divide(bytes, GB));
const unit = `GB`;
return new Size(result, unit);
}
if (bytes <= PB) {
// @returns TERABYTE
const result = divide(bytes, TB).toFixed(2);
const unit = `TB`;
return new Size(result, unit);
}
if (bytes >= PB) {
// @returns PETABYTE
const result = divide(bytes, PB).toFixed(2);
const unit = `PB`;
return new Size(result, unit);
}
}
Первоначально я использовал Aliceljm для проекта загрузки файлов, над которым я работал, но недавно столкнулся с проблемой, когда файл был 0.98kb
но читается как 1.02mb
, Вот обновленный код, который я сейчас использую.
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]
};
}
Вышеуказанное будет вызываться после добавления файла следующим образом:
// In this case `file.size` equals `26060275`
formatBytes(file.size);
// returns `{ size: 24.85, type: "mb" }`
Конечно, Windows читает файл как 24.8mb
но я в порядке с дополнительной точностью.
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]}`;
}
const byteConversion = (bytes: number, decimals = 2) => {
if (bytes === 0) return '0 B';
const kiloByte = 1000;
const decimal = decimals < 0 ? 0 : decimals;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i: number = Math.floor(Math.log(bytes) / Math.log(kiloByte));
return `${parseFloat((bytes / kiloByte ** i).toFixed(decimal))} ${sizes[i]}`;
};
функция getReadableByte(количество, десятичное значение = 0, уровень = 0) {let unitList = ["Байт", "КБ", "МБ", "ГБ", "ТБ", "PT"];
if(count >= 1024.0 && (level+1 < unitList.length)) {
return getReadableByte(count/1024, decimal, ++level)
}
return `${decimal ? (count).toFixed(decimal) : Math.round(count)}${unitList[level]}`
}
console.log (getReadableByte(120, 2))
Я обновляю ответ @Aliceljm здесь. Поскольку десятичное место имеет значение для 1,2-значных чисел, я округляю первое десятичное число и сохраняю первое десятичное. Для трехзначного числа я округляю число единиц и игнорирую все десятичные знаки.
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;
}
Это сработало для меня с реакцией, машинописным текстом.
export const FormatBytes = (bytes: number) => {
const units = ['b', 'kb', 'mb', 'gb', 'tb'];
let i = 0;
for (i; bytes >= 1024 && i < 4; i++) {
bytes /= 1024;
}
return `${bytes.toFixed(2)} ${units[i]}`;
};
Я просто хотел поделиться своим мнением. У меня была эта проблема, поэтому мое решение таково. Это преобразует более низкие единицы в более высокие и наоборот, просто предоставит аргументtoUnit
а также fromUnit
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);
}
Я получил идею отсюда
Вы можете использовать библиотеку симпатичных байтов .
Установить
npm install pretty-bytes
Применение
import prettyBytes from 'pretty-bytes';
prettyBytes(1337);
//=> '1.34 kB'
prettyBytes(100);
//=> '100 B'
// Display with units of bits
prettyBytes(1337, {bits: true});
//=> '1.34 kbit'
// Display file size differences
prettyBytes(42, {signed: true});
//=> '+42 B'
// Localized output using German locale
prettyBytes(1337, {locale: 'de'});
//=> '1,34 kB'
более гибкий и учитываемый максимальный потенциал в списке размеров (обновленный ответ l2aelba )
function formatBytes(bytes, decimals = 2, isBinary = false) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; // or ['B', 'KB', 'MB', 'GB', 'TB']
if (!+bytes) {
return `0 ${sizes[0]}`;
}
const inByte = isBinary ? 1024 : 1000;
const dm = decimals < 0 ? 0 : decimals;
const pow = Math.floor(Math.log(bytes) / Math.log(inByte));
const maxPow = Math.min(pow, sizes.length - 1);
return `${parseFloat((bytes / Math.pow(inByte, maxPow)).toFixed(dm))} ${
sizes[maxPow]
}`;
}
Я сделал алгоритм в 7 раз быстрее (работает как ниндзя):
... и вывод:
827.18 YB
827.18 YB
0.293 seconds
0.039 seconds
Иисус Христос!!!!
This is how a byte should be shown to a human:
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));
Это продолжение ответа с самым высоким рейтингом в настоящее время .
Пограничный случай
Я нашел пограничный случай: очень маленькое количество байтов ! В частности, когда количество байтов находится в диапазоне от -1 до 1 (исключительно).
Например, рассмотрим 0,25 байта. В этом случае,Math.floor(Math.log(0.25) / Math.log(1024))
вернется . С-1
не является допустимым индексом,formatBytes(0.25)
вернет что-то вроде «0,25 undefined».
Вот иллюстрация пограничного случая с использованием Wolfram Alpha.
Исправить
Я исправил это, добавив:
Math.max(0, Math.floor(Math.log(bytes) / Math.log(1024))
Math.max(0, ...)
гарантирует, что значение индекса всегда будет не меньше 0.
У меня была проблема с метаданными в файлах, возвращаемых с сервера, с разными размерами. Я использовал ответ @Alicejim и попытался сделать его более общим. поделитесь кодом здесь, может быть, это поможет кому-то.
enum SizeUnits {
Bytes = 'Bytes',
KB = 'KB',
MB = 'MB',
GB = 'GB',
TB = 'TB',
PB = 'PB',
EB = 'EB',
ZB = 'ZB',
YB = 'YB'
}
function convertSizeUnites(size: number, sourceSizeUnits: SizeUnits, targetSizeUnits: SizeUnits) {
const i = Object.keys(SizeUnits).indexOf(sourceSizeUnits);
const sizeInBytes = size * Math.pow(1024, i);
const j = Object.keys(SizeUnits).indexOf(targetSizeUnits);
return sizeInBytes / Math.pow(1024, j);
}
function formatSize(size: number, measureUnit: SizeUnits, decimals = 2) {
if (size === 0) return '0 Bytes';
const sizeInBytes = convertSizeUnites(size, measureUnit, SizeUnits.Bytes);
const dm = decimals < 0 ? 0 : decimals;
const i = Math.floor(Math.log(sizeInBytes) / Math.log(1024));
return parseFloat((sizeInBytes / Math.pow(1024, i)).toFixed(dm)) + ' ' +
Object.keys(SizeUnits)[i];
}
function bytes(value = 1, from = "mb", to = "b") {
const bytes = {'b':1,'kb':2,'mb':3,'gb':4,'tb':5,'pb':6,'eb':7,'zb':8,'yb':9};
let a = bytes[from], b = bytes[to];
if(a>b)
for(let i = 0;i < (a-b);i++)
value *=1024;
else
for(let i = 0;i < (b-a);i++)
value /=1024;
return value;
}
console.log(bytes());
console.log(bytes(1024, "kb", "mb"));
console.log(bytes(1, "gb", "kb"));