浏览代码

feat(server,web,mobile): Use binary prefixes for data sizes (#1009)

Kiel Hurley 2 年之前
父节点
当前提交
976d347623

+ 3 - 2
mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart

@@ -6,6 +6,7 @@ import 'package:immich_mobile/shared/models/asset.dart';
 import 'package:openapi/api.dart';
 import 'package:path/path.dart' as p;
 import 'package:latlong2/latlong.dart';
+import 'package:immich_mobile/utils/bytes_units.dart';
 
 class ExifBottomSheet extends ConsumerWidget {
   final Asset assetDetail;
@@ -162,7 +163,7 @@ class ExifBottomSheet extends ConsumerWidget {
                     ),
                     subtitle: exifInfo.exifImageHeight != null
                         ? Text(
-                            "${exifInfo.exifImageHeight} x ${exifInfo.exifImageWidth}  ${exifInfo.fileSizeInByte!}B ",
+                            "${exifInfo.exifImageHeight} x ${exifInfo.exifImageWidth}  ${formatBytes(exifInfo.fileSizeInByte!)} ",
                           )
                         : null,
                   ),
@@ -178,7 +179,7 @@ class ExifBottomSheet extends ConsumerWidget {
                         style: const TextStyle(fontWeight: FontWeight.bold),
                       ),
                       subtitle: Text(
-                        "ƒ/${exifInfo.fNumber}   1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)}   ${exifInfo.focalLength}mm   ISO${exifInfo.iso} ",
+                        "ƒ/${exifInfo.fNumber}   1/${(1 / (exifInfo.exposureTime ?? 1)).toStringAsFixed(0)}   ${exifInfo.focalLength} mm   ISO${exifInfo.iso} ",
                       ),
                     ),
                 ],

+ 15 - 13
mobile/lib/utils/bytes_units.dart

@@ -1,15 +1,17 @@
-
 String formatBytes(int bytes) {
-  if (bytes < 1000) {
-    return "$bytes B";
-  } else if (bytes < 1000000) {
-    final kb = (bytes / 1000).toStringAsFixed(1);
-    return "$kb kB";
-  } else if (bytes < 1000000000) {
-    final mb = (bytes / 1000000).toStringAsFixed(1);
-    return "$mb MB";
-  } else {
-    final gb = (bytes / 1000000000).toStringAsFixed(1);
-    return "$gb GB";
+  const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
+
+  int magnitude = 0;
+  double remainder = bytes.toDouble();
+  while (remainder >= 1024) {
+    if (magnitude + 1 < units.length) {
+      magnitude++;
+      remainder /= 1024;
+    }
+    else {
+      break;
+    }
   }
-}
+
+  return "${remainder.toStringAsFixed(magnitude == 0 ? 0 : 1)} ${units[magnitude]}";
+}

+ 1 - 1
server/apps/immich/src/modules/download/download.service.ts

@@ -35,7 +35,7 @@ export class DownloadService {
         fileCount++;
 
         // for easier testing, can be changed before merging.
-        if (totalSize > HumanReadableSize.GB * 20) {
+        if (totalSize > HumanReadableSize.GiB * 20) {
           complete = false;
           this.logger.log(
             `Archive size exceeded after ${fileCount} files, capping at ${totalSize} bytes (${asHumanReadable(

+ 20 - 26
server/apps/immich/src/utils/human-readable.util.ts

@@ -1,31 +1,25 @@
-const KB = 1000;
-const MB = KB * 1000;
-const GB = MB * 1000;
-const TB = GB * 1000;
-const PB = TB * 1000;
+const KiB = Math.pow(1024, 1);
+const MiB = Math.pow(1024, 2);
+const GiB = Math.pow(1024, 3);
+const TiB = Math.pow(1024, 4);
+const PiB = Math.pow(1024, 5);
 
-export const HumanReadableSize = { KB, MB, GB, TB, PB };
+export const HumanReadableSize = { KiB, MiB, GiB, TiB, PiB };
 
-export function asHumanReadable(bytes: number, precision = 1) {
-  if (bytes >= PB) {
-    return `${(bytes / PB).toFixed(precision)}PB`;
-  }
+export function asHumanReadable(bytes: number, precision = 1): string {
+  const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
 
-  if (bytes >= TB) {
-    return `${(bytes / TB).toFixed(precision)}TB`;
-  }
+	let magnitude = 0;
+	let remainder = bytes;
+	while (remainder >= 1024) {
+		if (magnitude + 1 < units.length) {
+			magnitude++;
+			remainder /= 1024;
+		}
+		else {
+			break;
+		}
+	}
 
-  if (bytes >= GB) {
-    return `${(bytes / GB).toFixed(precision)}GB`;
-  }
-
-  if (bytes >= MB) {
-    return `${(bytes / MB).toFixed(precision)}MB`;
-  }
-
-  if (bytes >= KB) {
-    return `${(bytes / KB).toFixed(precision)}KB`;
-  }
-
-  return `${bytes}B`;
+	return `${remainder.toFixed( magnitude == 0 ? 0 : precision )} ${units[magnitude]}`;
 }

+ 2 - 2
web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte

@@ -15,8 +15,8 @@
 		return name;
 	};
 
-	$: spaceUnit = stats.usage.slice(stats.usage.length - 2, stats.usage.length);
-	$: spaceUsage = stats.usage.slice(0, stats.usage.length - 2);
+	$: spaceUnit = stats.usage.split(' ')[1];
+	$: spaceUsage = stats.usage.split(' ')[0];
 </script>
 
 <div class="flex flex-col gap-5">

+ 4 - 29
web/src/lib/components/asset-viewer/detail-panel.svelte

@@ -8,6 +8,7 @@
 	import { createEventDispatcher, onMount } from 'svelte';
 	import { browser } from '$app/environment';
 	import { AssetResponseDto, AlbumResponseDto } from '@api';
+	import { getHumanReadableBytes } from '../../utils/byte-units';
 
 	type Leaflet = typeof import('leaflet');
 	type LeafletMap = import('leaflet').Map;
@@ -59,32 +60,6 @@
 	}
 
 	const dispatch = createEventDispatcher();
-	const getHumanReadableString = (sizeInByte: number) => {
-		const pepibyte = 1.126 * Math.pow(10, 15);
-		const tebibyte = 1.1 * Math.pow(10, 12);
-		const gibibyte = 1.074 * Math.pow(10, 9);
-		const mebibyte = 1.049 * Math.pow(10, 6);
-		const kibibyte = 1024;
-		// Pebibyte
-		if (sizeInByte >= pepibyte) {
-			// Pe
-			return `${(sizeInByte / pepibyte).toFixed(1)}PB`;
-		} else if (tebibyte <= sizeInByte && sizeInByte < pepibyte) {
-			// Te
-			return `${(sizeInByte / tebibyte).toFixed(1)}TB`;
-		} else if (gibibyte <= sizeInByte && sizeInByte < tebibyte) {
-			// Gi
-			return `${(sizeInByte / gibibyte).toFixed(1)}GB`;
-		} else if (mebibyte <= sizeInByte && sizeInByte < gibibyte) {
-			// Mega
-			return `${(sizeInByte / mebibyte).toFixed(1)}MB`;
-		} else if (kibibyte <= sizeInByte && sizeInByte < mebibyte) {
-			// Kibi
-			return `${(sizeInByte / kibibyte).toFixed(1)}KB`;
-		} else {
-			return `${sizeInByte}B`;
-		}
-	};
 
 	const getMegapixel = (width: number, height: number): number | undefined => {
 		const megapixel = Math.round((height * width) / 1_000_000);
@@ -143,13 +118,13 @@
 						{#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
 							{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
 								<p>
-									{getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}MP
+									{getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)} MP
 								</p>
 							{/if}
 
 							<p>{asset.exifInfo.exifImageHeight} x {asset.exifInfo.exifImageWidth}</p>
 						{/if}
-						<p>{getHumanReadableString(asset.exifInfo.fileSizeInByte)}</p>
+						<p>{getHumanReadableBytes(asset.exifInfo.fileSizeInByte)}</p>
 					</div>
 				</div>
 			</div>
@@ -162,7 +137,7 @@
 				<div>
 					<p>{asset.exifInfo.make || ''} {asset.exifInfo.model || ''}</p>
 					<div class="flex text-sm gap-2">
-						<p>{`f/${asset.exifInfo.fNumber}` || ''}</p>
+						<p>{`ƒ/${asset.exifInfo.fNumber}` || ''}</p>
 
 						{#if asset.exifInfo.exposureTime}
 							<p>{`1/${Math.floor(1 / asset.exifInfo.exposureTime)}`}</p>

+ 3 - 28
web/src/lib/components/shared-components/upload-panel.svelte

@@ -6,6 +6,8 @@
 	import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte';
 	import type { UploadAsset } from '$lib/models/upload-asset';
 	import { notificationController, NotificationType } from './notification/notification';
+	import { getHumanReadableBytes } from '../../utils/byte-units';
+
 	let showDetail = true;
 
 	let uploadLength = 0;
@@ -30,33 +32,6 @@
 		}
 	};
 
-	function getSizeInHumanReadableFormat(sizeInByte: number) {
-		const pepibyte = 1.126 * Math.pow(10, 15);
-		const tebibyte = 1.1 * Math.pow(10, 12);
-		const gibibyte = 1.074 * Math.pow(10, 9);
-		const mebibyte = 1.049 * Math.pow(10, 6);
-		const kibibyte = 1024;
-		// Pebibyte
-		if (sizeInByte >= pepibyte) {
-			// Pe
-			return `${(sizeInByte / pepibyte).toFixed(1)}PB`;
-		} else if (tebibyte <= sizeInByte && sizeInByte < pepibyte) {
-			// Te
-			return `${(sizeInByte / tebibyte).toFixed(1)}TB`;
-		} else if (gibibyte <= sizeInByte && sizeInByte < tebibyte) {
-			// Gi
-			return `${(sizeInByte / gibibyte).toFixed(1)}GB`;
-		} else if (mebibyte <= sizeInByte && sizeInByte < gibibyte) {
-			// Mega
-			return `${(sizeInByte / mebibyte).toFixed(1)}MB`;
-		} else if (kibibyte <= sizeInByte && sizeInByte < mebibyte) {
-			// Kibi
-			return `${(sizeInByte / kibibyte).toFixed(1)}KB`;
-		} else {
-			return `${sizeInByte}B`;
-		}
-	}
-
 	// Reactive action to get thumbnail image of upload asset whenever there is a new one added to the list
 	$: {
 		if ($uploadAssetsStore.length != uploadLength) {
@@ -140,7 +115,7 @@
 									<input
 										disabled
 										class="bg-gray-100 border w-full p-1 rounded-md text-[10px] px-2"
-										value={`[${getSizeInHumanReadableFormat(uploadAsset.file.size)}] ${
+										value={`[${getHumanReadableBytes(uploadAsset.file.size)}] ${
 											uploadAsset.file.name
 										}`}
 									/>

+ 16 - 0
web/src/lib/utils/byte-units.ts

@@ -0,0 +1,16 @@
+export function getHumanReadableBytes(bytes: number): string {
+	const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
+
+	let magnitude = 0;
+	let remainder = bytes;
+	while (remainder >= 1024) {
+		if (magnitude + 1 < units.length) {
+			magnitude++;
+			remainder /= 1024;
+		} else {
+			break;
+		}
+	}
+
+	return `${remainder.toFixed(magnitude == 0 ? 0 : 1)} ${units[magnitude]}`;
+}