ファイルサイズの数値をそのまま表示するのではなく、1.2 MB のように、直感的に理解できる単位へ変換する処理です。
if文を重ねて実装することも可能ですが、対数を利用するアプローチを紹介します。
FileFormatUtils クラス
import java.text.DecimalFormat;
public final class FileFormatUtils {
/**
* ファイルサイズ(バイト)を人が読みやすい単位(B, KB, MB...)に変換します。
*
* @param bytes ファイルサイズ(バイト単位)。nullも許容されます。
* @return フォーマットされた文字列。例: "1.2 MB"。
* 入力がnullまたは負の値の場合は "0 B" を返します。
*/
public static String formatFileSize(Long bytes) {
if (bytes == null || bytes < 0) {
return "0 B";
}
if (bytes < 1024) {
return bytes + " B";
}
// 単位
String[] units = new String[]{"B", "KB", "MB", "GB", "TB", "PB", "EB"};
// 単位のインデックスを算出
int digitGroups = (int) (Math.log10(bytes) / Math.log10(1024));
// 想定を超える巨大な値に対する安全対策
if (digitGroups >= units.length) {
digitGroups = units.length - 1;
}
// フォーマット
double formattedSize = bytes / Math.pow(1024, digitGroups);
String format = new DecimalFormat("#,##0.#").format(formattedSize);
return format + " " + units[digitGroups];
}
/**
* ユーティリティクラスのインスタンス化を防止します。
*/
private FileFormatUtils() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
}
対数の利用
ファイルサイズの単位が1024のべき乗(1024^0, 1024^1, 1024^2…)で変化することに着目します。この「べき乗」の指数部分を効率的に求める数学的な道具が「対数」です。
int digitGroups = (int) (Math.log10(bytes) / Math.log10(1024));
この一行は、Javaの標準ライブラリで提供されている常用対数(log10)と、対数の底の変換公式を利用して、「bytesは1024の何乗に最も近いか」を算出しています。結果として得られる整数 digitGroups は、units配列のインデックスに直接対応します。
このアプローチにより、コードはデータ(units配列)とロジック(対数計算)に分離され、将来的にZB(ゼタバイト)などの単位を追加する際も、配列に文字列を追加するだけで対応可能になります。
防御的プログラミング
下記は予期せぬ入力値を想定しています。
if (bytes == null || bytes < 0) {
return "0 B";
}
ファイルサイズが物理的に負の値になることはありませんが、プログラム上のエラーやデータ破損により、負の値が渡される可能性は否定できません。このガード節は、後続の Math.log10 が不正な値(負数)で失敗するのを防ぎ、メソッド全体の安定性を保証する重要な役割を果たします。
同様に、巨大な値が入力された場合に備え、digitGroupsが配列の範囲を超えないようにクリップする処理も、堅牢性を高める上で不可欠です。
外部ライブラリ
このような機能はフォーマットが少し異なりますが、 Apache Commons IO の FileUtils.byteCountToDisplaySize() で提供されています。
まとめ
ファイルサイズをフォーマットするコードの紹介でした。
もしプロジェクトで Apache Commons IO が利用可能なら、迷わず FileUtils.byteCountToDisplaySize() を使いましょう。
ライブラリを追加できない事情がある場合や、自作したいという場合は、このコードが参考になれば幸いです。


コメント