Format file size in Dart
Asked Answered
C

3

5

How to format a file size in Dart?

Input: 1000000

Expected output: 1 MB

The input could be either an int or a double for ease of use, and the result should be a String with only one decimal.

Cracking answered 12/5, 2022 at 16:25 Comment(0)
C
7

I made an extension method for this:

extension FileFormatter on num {
  String readableFileSize({bool base1024 = true}) {
    final base = base1024 ? 1024 : 1000;
    if (this <= 0) return "0";
    final units = ["B", "kB", "MB", "GB", "TB"];
    int digitGroups = (log(this) / log(base)).round();
    return NumberFormat("#,##0.#").format(this / pow(base, digitGroups)) +
        " " +
        units[digitGroups];
  }
}

You will need to use the intl package for the NumberFormat class.

You can display bits or bytes using the boolean base64.

Usage:

int myInt = 12345678;
double myDouble = 2546;
print('myInt: ${myInt.readableFileSize(base1024: false)}');
print('myDouble: ${myDouble.readableFileSize()}');

Output:

myInt: 12.3 MB
myDouble: 2.5 kB

Inspired by this SO answer.

Cracking answered 12/5, 2022 at 16:25 Comment(2)
This answer, though it works, it is not precise, I had a file with 65mb and your function returned "0.1 GB", which, in my case will is not useful, so I copied this function from this library pub.dev/packages/filesize, this is not the best implementation I have ever seen but it fill my use-case and is more precise than your function.Cummings
Comparing this solution with the SO answer referenced, two key points were missed. 1) dart:math's log fn is the natural log, NOT log base 10. Because there isn't a log10 fn in the math library, here is how you'd implement it double log10(final num x) => log(x) / ln10;. 2) rounding will not be precise enough, instead using .floor() will give the desired result.Skyscape
S
3

Since none of the above were satisfactory for me, I converted this function to a simpler to read, more flexible version:

extension FileSizeExtensions on num {
  /// method returns a human readable string representing a file size
  /// size can be passed as number or as string
  /// the optional parameter 'round' specifies the number of numbers after comma/point (default is 2)
  /// the optional boolean parameter 'useBase1024' specifies if we should count in 1024's (true) or 1000's (false). e.g. 1KB = 1024B (default is true)
  String toHumanReadableFileSize({int round = 2, bool useBase1024 = true}) {
    const List<String> affixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];

    num divider = useBase1024 ? 1024 : 1000;

    num size = this;
    num runningDivider = divider;
    num runningPreviousDivider = 0;
    int affix = 0;

    while (size >= runningDivider && affix < affixes.length - 1) {
      runningPreviousDivider = runningDivider;
      runningDivider *= divider;
      affix++;
    }

    String result = (runningPreviousDivider == 0 ? size : size / runningPreviousDivider).toStringAsFixed(round);

    //Check if the result ends with .00000 (depending on how many decimals) and remove it if found.
    if (result.endsWith("0" * round)) result = result.substring(0, result.length - round - 1);

    return "$result ${affixes[affix]}";
  }
}

Sample output:

1024 = 1 KB  
800 = 800 B  
8126 = 7.94 KB  
10247428 = 9.77 MB  
Sylvestersylvia answered 14/8, 2023 at 21:7 Comment(0)
O
0

Fixed leb's version and added international units:

import 'package:intl/intl.dart';
import 'dart:math';
import 'package:test/test.dart';

extension FileFormatter on num {
  // Converts x to a double and returns the common logarithm of the value.
  // WARNING!!! This function is not such accurate as low-level implementation! WARNING!!! Some unit-tests fail because of that!
  // From: https://pub.dev/documentation/dart_numerics/latest/dart_numerics/log10.html
  double log10(num x) => log(x) / ln10;

  String readableFileSize({bool base1024 = true}) {
    if (this <= 0) return "0";

    final base = base1024 ? 1024 : 1000;
    final units = base1024
        ? ["Bi", "KiB", "MiB", "GiB", "TiB"]
        : ["B", "kB", "MB", "GB", "TB"];

    int digitGroups = (log10(this) / log10(base)).floor();
    return "${NumberFormat("#,##0.#").format(this / pow(base, digitGroups))} ${units[digitGroups]}";
  }
}

void main() async {
  test('', () => expect(1000.readableFileSize(base1024: false), '1 kB'));
  test('', () => expect(1023.readableFileSize(), '1,023 Bi'));
  test('', () => expect(1024.readableFileSize(), '1 KiB'));
  test('', () => expect((1024 * 1023).readableFileSize(), '1,023 KiB'));
  test('', () => expect((1024 * 1024 * 1023).readableFileSize(), '1,023 MiB'));
  test('', () => expect((1024 * 1024 * 1024 * 1023).readableFileSize(), '1,023 GiB'));
  test('', () => expect( (1024 * 1024 * 1024 * 1024 * 1023).readableFileSize(), '1,023 TiB'));
}
Osprey answered 12/5, 2022 at 16:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.