Java Tips | 基本ユーティリティ:ファイル名生成

Java Java
スポンサーリンク

ファイル名生成は「衝突させずに意味を持たせる」ための技

業務システムでは、ログファイル、レポート出力、アップロードファイルの保存先など、「新しいファイル名を決める」場面が山ほどあります。
ここで適当に "output.txt" みたいな名前を使い回すと、上書き事故が起きたり、どのジョブが出したファイルか分からなくなったりします。

だからこそ、「ファイル名生成」をユーティリティとして整理しておくと、
「衝突しない」「意味が読み取れる」「OS に優しい」ファイル名を、毎回同じルールで作れるようになります。


基本方針:「いつ・誰の・何の」ファイルかを名前に埋め込む

最低限入れたい情報を決める

実務でのファイル名は、だいたい次のような要素で構成すると分かりやすくなります。

  • 何のファイルか(prefix)
  • いつ作られたか(タイムスタンプ)
  • 誰/何の処理が作ったか(ID や種別)
  • 拡張子

これをコードで表現すると、例えばこんなイメージです。

report-20250127-101530-job123.csv
upload-20250127-101530-8f3a2c1e.png

「何となくそれっぽい名前」ではなく、「どの情報を入れるか」を先に決めてから、ユーティリティで組み立てるのがポイントです。


実務で使えるシンプルなファイル名生成ユーティリティ

日時+連番で「その日中は衝突しない」名前を作る

まずは、日付・時刻と連番を組み合わせた、シンプルなユーティリティを作ってみます。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicInteger;

public final class FileNameGenerator {

    private FileNameGenerator() {}

    private static final DateTimeFormatter DATE_TIME =
            DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss");
    private static final AtomicInteger SEQ = new AtomicInteger(0);

    public static String generate(String prefix, String extension) {
        String timestamp = LocalDateTime.now().format(DATE_TIME);
        int seq = SEQ.updateAndGet(i -> (i >= 9999) ? 0 : i + 1);

        StringBuilder sb = new StringBuilder();
        if (prefix != null && !prefix.isBlank()) {
            sb.append(prefix).append("-");
        }
        sb.append(timestamp)
          .append("-")
          .append(String.format("%04d", seq));

        if (extension != null && !extension.isBlank()) {
            sb.append(".").append(extension);
        }
        return sb.toString();
    }
}
Java

使い方はこうです。

String name1 = FileNameGenerator.generate("report", "csv");
String name2 = FileNameGenerator.generate("report", "csv");
System.out.println(name1); // 例: report-20250127-110305-0001.csv
System.out.println(name2); // 例: report-20250127-110305-0002.csv
Java

ここで深掘りしたい重要ポイントは、「“時間+連番”で衝突確率を下げている」ことです。
同じ秒の間に複数回呼ばれても、連番が増えるのでファイル名がかぶりません。
また、日付・時刻を入れておくことで、「いつ出力されたファイルか」が名前から一目で分かります。


例題:レポート出力ファイル名を一元管理する

呼び出し側から「命名ルール」を追い出す

レポート出力ジョブを考えてみます。
ユーティリティがないと、ついこんなコードを書きがちです。

// よくない例
String fileName = "report-" + System.currentTimeMillis() + ".csv";
Java

これを、先ほどのユーティリティに置き換えます。

import java.nio.file.Files;
import java.nio.file.Path;

public class ReportJob {

    private final Path outputDir;

    public ReportJob(Path outputDir) {
        this.outputDir = outputDir;
    }

    public Path run() throws Exception {
        String fileName = FileNameGenerator.generate("report", "csv");
        Path output = outputDir.resolve(fileName);

        Files.writeString(output, "id,name\n1,foo\n2,bar\n");
        return output;
    }
}
Java

ここでのポイントは、「“ファイル名の作り方”を ReportJob から追い出している」ことです。
ReportJob は「レポートを出力する」ことだけに集中し、
「どういうルールでファイル名を付けるか」は FileNameGenerator に任せています。

こうしておくと、「タイムスタンプのフォーマットを変えたい」「連番をやめて UUID にしたい」といった変更も、
ユーティリティ側だけを直せばよくなり、業務ロジックを触らずに済みます。


UUID を使った「ほぼ絶対にかぶらない」ファイル名

衝突リスクを極限まで下げたいとき

「とにかくかぶらなければよい」「人間が見て意味を読む必要はない」という場面では、UUID を使うのも有効です。

import java.util.UUID;

public final class UuidFileNames {

    private UuidFileNames() {}

    public static String random(String prefix, String extension) {
        String uuid = UUID.randomUUID().toString().replace("-", "");

        StringBuilder sb = new StringBuilder();
        if (prefix != null && !prefix.isBlank()) {
            sb.append(prefix).append("-");
        }
        sb.append(uuid);
        if (extension != null && !extension.isBlank()) {
            sb.append(".").append(extension);
        }
        return sb.toString();
    }
}
Java

使い方はこうです。

String name = UuidFileNames.random("upload", "png");
// 例: upload-3f9a1c2b4d6e7f8090a1b2c3d4e5f607.png
Java

ここでの重要ポイントは、「“人間にとっての意味”より“衝突しないこと”を優先している」ことです。
アップロードファイルの保存名など、「ユーザーには見せない内部的な名前」には、このスタイルが向いています。
一方で、運用者が直接ファイルを見て調査するような場面では、日時や種別を含めたほうが親切です。


OS に優しいファイル名にするための注意点

使わないほうがいい文字を避ける

Windows などでは、ファイル名に使えない文字がいくつかあります。

/ \ : * ? " < > | など

また、スペースや全角記号を多用すると、コマンドラインやスクリプトから扱いづらくなります。

ユーティリティ側で、「危ない文字を安全な文字に置き換える」処理を入れておくと安心です。

public final class FileNameSanitizer {

    private FileNameSanitizer() {}

    public static String sanitize(String raw) {
        if (raw == null || raw.isBlank()) {
            return "";
        }
        String s = raw.trim();
        s = s.replaceAll("[\\\\/:*?\"<>|]", "_");
        return s;
    }
}
Java

これをファイル名生成の前処理として使います。

String safePrefix = FileNameSanitizer.sanitize(userInputPrefix);
String name = FileNameGenerator.generate(safePrefix, "txt");
Java

ここで深掘りしたいのは、「“ユーザー入力をそのままファイル名に使わない”という習慣」です。
ユーザーが自由に入力できる文字列は、必ずサニタイズしてからファイル名に組み込むようにすると、
OS 依存のトラブルや、意図しないパス解釈のリスクを減らせます。


まとめ:ファイル名生成ユーティリティで身につけたい感覚

ファイル名生成は、「とりあえず適当な文字列をくっつける」ではなく、
「衝突させずに、後から見ても意味が分かる名前を、毎回同じルールで作る」ための技です。

押さえておきたい感覚はこうです。

ファイル名には「何のファイルか」「いつ作られたか」「必要なら ID」を埋め込む。
時間+連番、または UUID を使って、同時実行でもかぶりにくい名前にする。
命名ルールはユーティリティに閉じ込め、業務ロジックからは追い出す。
ユーザー入力は必ずサニタイズしてからファイル名に組み込む。

タイトルとURLをコピーしました