-
-
Save JonCatmull/ecdf9441aaa37336d9ae2c7f9cb7289a to your computer and use it in GitHub Desktop.
/** | |
* @license | |
* Copyright (c) 2019 Jonathan Catmull. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
* SOFTWARE. | |
*/ | |
import { Pipe, PipeTransform } from '@angular/core'; | |
type unit = 'bytes' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB'; | |
type unitPrecisionMap = { | |
[u in unit]: number; | |
}; | |
const defaultPrecisionMap: unitPrecisionMap = { | |
bytes: 0, | |
KB: 0, | |
MB: 1, | |
GB: 1, | |
TB: 2, | |
PB: 2 | |
}; | |
/* | |
* Convert bytes into largest possible unit. | |
* Takes an precision argument that can be a number or a map for each unit. | |
* Usage: | |
* bytes | fileSize:precision | |
* @example | |
* // returns 1 KB | |
* {{ 1500 | fileSize }} | |
* @example | |
* // returns 2.1 GB | |
* {{ 2100000000 | fileSize }} | |
* @example | |
* // returns 1.46 KB | |
* {{ 1500 | fileSize:2 }} | |
*/ | |
@Pipe({ name: 'fileSize' }) | |
export class FileSizePipe implements PipeTransform { | |
private readonly units: unit[] = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; | |
transform(bytes: number = 0, precision: number | unitPrecisionMap = defaultPrecisionMap): string { | |
if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) return '?'; | |
let unitIndex = 0; | |
while (bytes >= 1024) { | |
bytes /= 1024; | |
unitIndex++; | |
} | |
const unit = this.units[unitIndex]; | |
if (typeof precision === 'number') { | |
return `${bytes.toFixed(+precision)} ${unit}`; | |
} | |
return `${bytes.toFixed(precision[unit])} ${unit}`; | |
} | |
} |
Thank you for this great piece of code 😊👍
I just optimized it a little bit for me by adding the standard decimal pipe to convert the number to local format, so it looks better on my German project 😉
https://gist.github.com/msteini82/6b77f92feda1f6f8286bc1bc7e6fd21b
Thanks, everyone for your comments! Sorry, I haven't been on here and the emails got overlooked.
@coreyog, @AarjavP, @donmccurdy, @msteini82
Thank you for your suggestions, I will definitely look to incorporate these and repost. Then perhaps this is worth putting in an npm module to easily import into your projects if anyone has a suggestion on the name then drop a comment below.
Thanks, again.
A simple suggestion.
if (unit == 0) precision = 0;
after the while loop so you don't end up with "38.00 bytes."I guess this wouldn't help if you were averaging file sizes or something but I bet total file size is it's more popular use.
And what about this one...
let unit = 0, division = bytes;
while ( division >= 1024 ) {
division /= 1024;
unit ++;
}
if (bytes % 1024 <= 10) precision = 0;
I found it cleaner to change:
while ( bytes >= 1024 ) {
to:
while ( bytes >= 1000 ) {
to avoid having something like 1021 kB. This would be rounded down to 1 MB which looks better.
Added a file size precision map so you can have more finite control over what precision you want per unit.
It still accepts a number for the same precision across all units.
There is a small issue if the bytes
is int value, will raise error bytes.toFixed is not a function
, a quick fix is convert to float before run toFixed
, bytes = parseFloat(bytes.toString());
import { Pipe, PipeTransform } from '@angular/core';
const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const FILE_SIZE_UNITS_LONG = ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Pettabytes', 'Exabytes', 'Zettabytes', 'Yottabytes'];
@Pipe({
name: 'formatFileSize'
})
export class FormatFileSizePipe implements PipeTransform {
transform(sizeInBytes: number, longForm: boolean): string {
const units = longForm
? FILE_SIZE_UNITS_LONG
: FILE_SIZE_UNITS;
let power = Math.round(Math.log(sizeInBytes) / Math.log(1024));
power = Math.min(power, units.length - 1);
const size = sizeInBytes / Math.pow(1024, power); // size in new units
const formattedSize = Math.round(size * 100) / 100; // keep up to 2 decimals
const unit = units[power];
return `${formattedSize} ${unit}`;
}
}
}
nice, thanks
Another example would be dates. If you have different time formats of dates in your app logic it is often a nightmare and it is bug-prone. So in your application logic you stick to one date format (ISO, UTC, whatever) and on your user screen you use pipes like DatePipe to show the final "display" value of a date. Many formats for the user (pipes), but in the background one format to rule them all (ISO, UTC, whatever).