-
-
Save lanqy/5193417 to your computer and use it in GitHub Desktop.
// from http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/ | |
function bytesToSize(bytes) { | |
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; | |
if (bytes == 0) return 'n/a'; | |
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); | |
if (i == 0) return bytes + ' ' + sizes[i]; | |
return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]; | |
}; |
function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
if (bytes === 0) return 'n/a'
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10)
if (i === 0) return ${bytes} ${sizes[i]})
return ${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}
}
With little typo fixed on line 5:
function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
if (bytes === 0) return 'n/a'
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10)
if (i === 0) return `${bytes} ${sizes[i]}`
return `${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}`
}
Some may doesn't like having a seperator so:
function bytesToSize(bytes, seperator = "") {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
if (bytes == 0) return 'n/a'
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10)
if (i === 0) return `${bytes}${seperator}${sizes[i]}`
return `${(bytes / (1024 ** i)).toFixed(1)}${seperator}${sizes[i]}`
}
console.log( bytesToSize(2659633) ); // 2.5MB
console.log( bytesToSize(2659633, " ") ); // 2.5 MB
console.log( bytesToSize(2659633, "-") ); // 2.5-MB
I had a need to format negative values, but Math.log(x)
returns NaN
if x
is negative, so I pass bytes
through Math.abs()
first.
function bytesToSize(bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) return 'n/a';
const i = parseInt(Math.floor(Math.log(Math.abs(bytes)) / Math.log(1024)), 10);
if (i === 0) return `${bytes} ${sizes[i]}`;
return `${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}`;
}
console.log(bytesToSize(1234567)); // 1.2 MB
console.log(bytesToSize(-1234567)); // -1.2 MB
@jedfoster I used your code, but just pointing out something... If you're only rendering "Bytes", there's a dangling closing parenthesis.
e.g. "Bytes)"
- 1 redundant line removed
- added suffix param
- fixed error if size more then 1023 TB
function prettySize(bytes, separator = '', postFix = '') {
if (bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10), sizes.length - 1);
return `${(bytes / (1024 ** i)).toFixed(i ? 1 : 0)}${separator}${sizes[i]}${postFix}`;
}
return 'n/a';
}
thank you
fixed a type error on line 4, Argument of type 'number' is not assignable to parameter of type 'string'
prettySize(bytes, separator = '', postFix = '') {
if (bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10), sizes.length - 1);
return `${(bytes / (1024 ** i)).toFixed(i ? 1 : 0)}${separator}${sizes[i]}${postFix}`;
}
return 'n/a';
}
A little bit of code for people who want the reverse.
Basically adapted this from https://stackoverflow.com/a/31625253/104461
function printpower (n, base, power) {
if (base === 2) { // 1 << power is approx 10x faster than Math.pow(2, power)
console.log(n * (1 << power))
} else {
console.log(n * Math.pow(base, power))
}
}
const humanReadable = '86 MB'
const [n, abbreviation] = humanReadable.split(/\s+/)
if (abbreviation) {
if (/K(iB)?$/.test(abbreviation)) {
printpower(n, 2, 10)
} else if (/M(iB)?$/.test(abbreviation)) {
printpower(n, 2, 20)
} else if (/G(iB)?$/.test(abbreviation)) {
printpower(n, 2, 30)
} else if (/T(iB)?$/.test(abbreviation)) {
printpower(n, 2, 40)
} else if (/KB$/.test(abbreviation)) {
printpower(n, 10, 3)
} else if (/MB$/.test(abbreviation)) {
printpower(n, 10, 6)
} else if (/GB$/.test(abbreviation)) {
printpower(n, 10, 9)
} else if (/TB$/.test(abbreviation)) {
printpower(n, 10, 12)
}
} else {
console.log(n)
}
Edit: the if
statement is probably pretty expensive, and so are the Regular Expressions (at a minimum, should be compiled once as const
variables), so there is room for incremental optimization!
Thanks all of you
TypeScript version:
export function bytesToSize(bytes: number): string {
const sizes: string[] = ['Bytes', 'KB', 'MB', 'GB', 'TB']
if (bytes === 0) return 'n/a'
const i: number = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString())
if (i === 0) return `${bytes} ${sizes[i]}`
return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`
}
Strength and power of open source community. I'm shocked and impressed. Thanks to all, guys.
function bytesToSize(bytes) {
const units = ["byte", "kilobyte", "megabyte", "terabyte", "petabyte"];
const unit = Math.floor(Math.log(bytes) / Math.log(1024));
return new Intl.NumberFormat("en", {style: "unit", unit: units[unit]}).format(bytes / 1024 ** unit);
}
To expand on the typescript version, I don't think the parseInt(...)
is required. Also, in the unlikely case that you exceed 999.9 TB
, you end up in xxx.x undefined
territory.
Here is a fixed Typescript version:
export function bytesToSize(bytes: number): string {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) return 'n/a';
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), sizes.length - 1);
if (i === 0) return `${bytes} ${sizes[i]}`;
return `${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}`;
}
With Jest spec:
describe('bytesToSize', () => {
test.each([
[0, 'n/a'],
[1, '1 Bytes'],
[2000, '2.0 KB'],
[30000, '29.3 KB'],
[400000, '390.6 KB'],
[5000000, '4.8 MB'],
[60000000, '57.2 MB'],
[700000000, '667.6 MB'],
[8000000000, '7.5 GB'],
[90000000000, '83.8 GB'],
[100000000000, '93.1 GB'],
[1100000000000, '1.0 TB'],
[12000000000000, '10.9 TB'],
[130000000000000, '118.2 TB'],
[1400000000000000, '1273.3 TB'],
[15000000000000000, '13642.4 TB'],
])('.bytesToSize(%p)', (
val: number,
expected: string,
) => {
expect(
bytesToSize(val),
).toBe(expected);
});
});
Using the Intl.NumberFormat
for local friendly conversions
export function bytesToSize(bytes: number): string {
const units = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte'];
const navigatorLocal = navigator.languages && navigator.languages.length >= 0 ? navigator.languages[0] : 'en-US'
const unitIndex = Math.max(0, Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1));
return Intl.NumberFormat(navigatorLocal, {
style: 'unit',
unit : units[unitIndex]
}).format(bytes / (1024 ** unitIndex))
}
The sizes should be named Mebibyte (MiB), Gibibyte (GiB) etc if you use 1024 for the calculation.
Or if you want to calculate Megabyte, Gigabyte etc. replace the '1024' in the code by '1000'.
@HaukeHa Thank you for noticing 👍
I use the following code:
export function convertBytes(bytes: number, options: { useBinaryUnits?: boolean; decimals?: number } = {}): string {
const { useBinaryUnits = false, decimals = 2 } = options;
if (decimals < 0) {
throw new Error(`Invalid decimals ${decimals}`);
}
const base = useBinaryUnits ? 1024 : 1000;
const units = useBinaryUnits
? ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]
: ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(base));
return `${(bytes / Math.pow(base, i)).toFixed(decimals)} ${units[i]}`;
}
and basic tests:
import { describe, expect } from "vitest";
import { convertBytes } from "./bytes";
describe("convertBytes", () => {
const fileSizeInBytes = 2048;
describe("default conversion", () => {
it("should convert to KB", () => {
const resultKB = convertBytes(fileSizeInBytes);
expect(resultKB).toBe("2.05 KB");
});
it("should convert to MB", () => {
const resultMB = convertBytes(fileSizeInBytes * 1000);
expect(resultMB).toBe("2.05 MB");
});
it("should throw an error for invalid decimals", () => {
expect(() => convertBytes(fileSizeInBytes, { decimals: -1 })).toThrowError("Invalid decimals -1");
});
});
describe("conversion to binary units", () => {
it("should convert to KiB", () => {
const resultKiB = convertBytes(fileSizeInBytes, { useBinaryUnits: true });
expect(resultKiB).toBe("2.00 KiB");
});
it("should convert to MiB", () => {
const resultMiB = convertBytes(fileSizeInBytes * 1024, { useBinaryUnits: true });
expect(resultMiB).toBe("2.00 MiB");
});
});
it("should handle very large bytes (Number.MAX_SAFE_INTEGER)", () => {
const result = convertBytes(Number.MAX_SAFE_INTEGER);
expect(result).toBe("9.01 PB");
});
});
const units = ["byte", "kilobyte", "megabyte", "terabyte", "petabyte"];
you lost "gigabyte"
ES6 and airbnb's eslint compliant version: