ファイルサイズ取得は「重さを意識した設計」をするための技
業務システムでは、「このファイル、本当にメモリに全部載せて大丈夫?」「アップロードされたファイルが想定より巨大じゃない?」
といった“重さ”の感覚がとても大事になります。
その入口になるのが「ファイルサイズ取得」です。
単に「何バイトか知る」だけでなく、「上限チェック」「ログ出力」「処理方針の分岐(全部読むか、ストリームで読むか)」など、設計の判断材料になります。
基本:Files.size(Path) でバイト数を取得する
一番シンプルなサンプル
Java 7 以降なら、java.nio.file.Files.size を使うのが基本です。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class FileSizeBasic {
public static void main(String[] args) throws IOException {
Path path = Path.of("data/input.csv");
long size = Files.size(path);
System.out.println("size (bytes) = " + size);
}
}
JavaFiles.size(path) は、そのパスが指すファイルのサイズを「バイト数」で返します。
戻り値は long なので、2GB を超えるような大きなファイルでも扱えます。
ここでの重要ポイントは、「単位は“バイト”である」ことです。
人間にとっては KB、MB、GB のほうが分かりやすいので、実務では「バイト → 人間向け表記」に変換するユーティリティとセットで使うことが多いです。
実務で使える「ファイルサイズ取得ユーティリティ」の最小形
size と、人間向けの表示をセットにする
まずは、サイズ取得と表示用フォーマットを一箇所にまとめたユーティリティを作ってみます。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public final class FileSizes {
private FileSizes() {}
public static long size(Path path) throws IOException {
return Files.size(path);
}
public static String humanReadable(long bytes) {
if (bytes < 1024) {
return bytes + " B";
}
double kb = bytes / 1024.0;
if (kb < 1024) {
return String.format("%.1f KB", kb);
}
double mb = kb / 1024.0;
if (mb < 1024) {
return String.format("%.1f MB", mb);
}
double gb = mb / 1024.0;
return String.format("%.1f GB", gb);
}
public static String describe(Path path) throws IOException {
long size = size(path);
return path + " (" + humanReadable(size) + ")";
}
}
Java使う側はこう書けます。
Path path = Path.of("data/input.csv");
long size = FileSizes.size(path);
System.out.println("size = " + size + " bytes");
System.out.println("human = " + FileSizes.humanReadable(size));
System.out.println("desc = " + FileSizes.describe(path));
Javaここで深掘りしたいポイントは、「“生のバイト数”と“人間向け表記”を分けて扱う」ことです。
ロジック(上限チェックなど)はバイト数で行い、ログや画面表示は人間向け表記を使う、という役割分担にすると、コードが読みやすくなります。
例題:アップロードファイルのサイズ上限チェック
「大きすぎるファイルはそもそも受け付けない」
よくある要件として、「アップロードできるファイルサイズは最大 10MB まで」といった制限があります。
これをサーバー側でチェックするユーティリティを考えてみます。
ここでは「一度ファイルとして保存された後にチェックする」パターンにします。
import java.io.IOException;
import java.nio.file.Path;
public final class UploadValidator {
private UploadValidator() {}
private static final long MAX_SIZE_BYTES = 10L * 1024 * 1024; // 10MB 相当
public static void validateSize(Path uploadedFile) throws IOException {
long size = FileSizes.size(uploadedFile);
if (size > MAX_SIZE_BYTES) {
throw new IllegalArgumentException(
"アップロードファイルが大きすぎます: " +
FileSizes.humanReadable(size) +
" (上限 " + FileSizes.humanReadable(MAX_SIZE_BYTES) + ")");
}
}
}
Java使う側はこうです。
Path uploaded = Path.of("work/upload.tmp");
UploadValidator.validateSize(uploaded);
// ここから先は「サイズ上限を満たしている」前提で処理を書ける
Javaここでの重要ポイントは、「上限値もバイト数で持ち、メッセージだけ人間向け表記にしている」ことです。10 * 1024 * 1024 のように「バイト数としての上限」をコードに書いておくと、
「12MB に変えたい」といったときも、計算がしやすくなります。
また、例外メッセージに「実際のサイズ」と「上限」を両方、人間向け表記で含めておくと、
ログを見たときに「どれくらいオーバーしていたのか」が一目で分かります。
例題:大きなファイルは「読み方」を変える
サイズを見て「全部読むか、ストリームで読むか」を決める
例えば、「CSV ファイルを読み込んで処理する」というバッチを考えます。
小さなファイルなら全部メモリに読み込んでも問題ありませんが、数百 MB のファイルを丸ごと読み込むと、メモリを圧迫します。
そこで、「ファイルサイズを見て、読み方を変える」という設計ができます。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class CsvProcessor {
private static final long THRESHOLD_BYTES = 5L * 1024 * 1024; // 5MB
public void process(Path csv) throws IOException {
long size = FileSizes.size(csv);
System.out.println("processing " + FileSizes.describe(csv));
if (size <= THRESHOLD_BYTES) {
processSmall(csv);
} else {
processLarge(csv);
}
}
private void processSmall(Path csv) throws IOException {
List<String> lines = Files.readAllLines(csv);
// 全行メモリに載せて処理
}
private void processLarge(Path csv) throws IOException {
try (var stream = Files.lines(csv)) {
stream.forEach(line -> {
// 1 行ずつストリームで処理
});
}
}
}
Javaここで深掘りしたいのは、「ファイルサイズ取得を“設計の分岐条件”として使っている」点です。
単に「大きいか小さいかを知る」だけでなく、「読み方を変える」「処理方法を変える」判断材料にしているわけです。
この感覚を持てると、「とりあえず全部 readAllBytes」から一歩抜け出して、
「サイズを見てから戦略を選ぶ」という、実務寄りの設計ができるようになります。
ディレクトリ配下の合計サイズを出す
Files.walk と組み合わせて「フォルダの重さ」を測る
「このディレクトリ配下、全部でどれくらいのサイズか知りたい」という場面もあります。
バックアップ対象の重さを見積もったり、古いログを消す基準にしたりするイメージです。
import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
public final class DirectorySizes {
private DirectorySizes() {}
public static long totalSize(Path root) throws IOException {
if (root == null || !Files.exists(root)) {
return 0L;
}
AtomicLong total = new AtomicLong(0L);
try (Stream<Path> stream = Files.walk(root)) {
stream
.filter(Files::isRegularFile)
.forEach(p -> {
try {
total.addAndGet(Files.size(p));
} catch (IOException e) {
throw new RuntimeException("サイズ取得中にエラー: " + p, e);
}
});
}
return total.get();
}
}
Java使う側はこうです。
Path logsDir = Path.of("logs");
long total = DirectorySizes.totalSize(logsDir);
System.out.println("logs total = " + FileSizes.humanReadable(total));
Javaここでのポイントは、「ファイルサイズ取得は“単体”だけでなく、“合計”にも使える」ということです。Files.walk と組み合わせると、「このフォルダ、実は 10GB あるじゃん」という現実を数字で突きつけてくれます。
そこから「古い日付のログを消そう」「圧縮しよう」といった次のアクションにつなげられます。
ファイルサイズ取得の“落とし穴”と注意点
サイズ取得も I/O であり、コストゼロではない
Files.size はメタデータを読むだけなので、ファイル内容を全部読むよりは軽いですが、
それでもファイルシステムへのアクセスが発生する I/O です。
大量のファイルに対して一気に Files.size を呼ぶと、それなりに時間がかかります。
監視や定期バッチで「ディレクトリ配下の合計サイズ」を頻繁に計算する場合は、
実行タイミングや頻度を考えたり、結果をキャッシュしたりする工夫も検討したほうがよいです。
ネットワークドライブやクラウドストレージでは揺らぎがある
NFS やクラウドストレージ(マウントされたオブジェクトストレージなど)の場合、
サイズ取得が遅かったり、一時的にエラーになったりすることがあります。
実務では、「サイズ取得に失敗したらどうするか」を決めておくことが大事です。
「失敗したら 0 とみなす」のか、「処理自体をエラーにする」のか、「警告ログだけ出して続行する」のか、
ユーティリティ側や呼び出し側で方針をはっきりさせておくと、運用で迷いません。
まとめ:ファイルサイズ取得ユーティリティで身につけたい感覚
ファイルサイズ取得は、「ただ数字を知る」ためではなく、「その数字をもとに設計や運用の判断をする」ための技です。
押さえておきたい感覚はこうです。
ファイルサイズは Files.size(Path) でバイト数として取得し、ロジックはバイト単位で書く。
人間向けには KB/MB/GB に変換するユーティリティを用意し、ログやメッセージで使う。
アップロードやバッチ処理では、「サイズ上限チェック」や「読み方の切り替え」の判断材料として使う。
ディレクトリ配下の合計サイズを出すことで、「どこがディスクを食っているか」を数字で把握する。
サイズ取得も I/O であり、失敗や遅延の可能性がある前提で、エラー時の扱いを設計しておく。

