Java Tips | 基本ユーティリティ:一時ファイル作成

Java Java
スポンサーリンク

一時ファイル作成は「一時的な作業領域を安全に借りる」技

業務システムでは、「大きなデータを一度ファイルに落としてから処理したい」「ZIP を展開して中身を検査したい」「外部システムに渡す前に一時的にファイルを組み立てたい」といった、“一時的な作業領域”が必要な場面がよくあります。
このときに便利なのが「一時ファイル」です。

一時ファイルを正しく扱えると、メモリを無駄に使わずに済み、障害調査のときに中身を確認しやすくなります。
逆に、適当に作って消し忘れると、ディスクを食いつぶしたり、機密情報を残しっぱなしにしたりと、実務ではかなり危険です。
だからこそ、「一時ファイル作成ユーティリティ」をきちんと設計しておく価値があります。


基本:Files.createTempFile で一時ファイルを作る

最小のサンプルで動きをつかむ

Java 7 以降では、java.nio.file.FilescreateTempFile を使うのが基本です。

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

public class TempFileBasic {

    public static void main(String[] args) throws IOException {
        Path temp = Files.createTempFile("myapp-", ".tmp");
        System.out.println("一時ファイル: " + temp);
    }
}
Java

第一引数がプレフィックス(ファイル名の先頭)、第二引数がサフィックス(拡張子)です。
実際のファイル名は OS が一意になるように決めてくれるので、myapp-1234567890.tmp のような名前になります。

保存場所は、OS が用意している「一時ディレクトリ」です。
Windows なら C:\Users\...\AppData\Local\Temp、Linux なら /tmp など、環境によって異なりますが、createTempFile がよしなに選んでくれます。

ここでの重要ポイントは、「自分で“かぶらないファイル名”を考えなくてよい」ことです。
マルチスレッドや複数プロセスが同時に動いていても、createTempFile は衝突しないように名前を付けてくれます。


一時ファイルの「作る」と「消す」をセットで考える

作るのは簡単、難しいのは「いつ・どう消すか」

一時ファイルで一番大事なのは、「作り方」よりも「消し方」です。
作るだけ作って消し忘れると、時間とともに一時ディレクトリがゴミだらけになり、ディスクフルの原因になります。

まずは、典型的な「作って、使って、消す」流れを見てみましょう。

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

public class TempFileUseAndDelete {

    public static void main(String[] args) throws IOException {
        Path temp = Files.createTempFile("myapp-", ".csv");
        try {
            // 一時ファイルに何か書く
            Files.writeString(temp, "id,name\n1,foo\n2,bar\n");

            // ここで外部システムに渡したり、別処理で読んだりする
            String content = Files.readString(temp);
            System.out.println(content);
        } finally {
            // 使い終わったら削除
            Files.deleteIfExists(temp);
        }
    }
}
Java

ここで深掘りしたいのは、「try-finally で“必ず消す”を保証する」という発想です。
途中で例外が起きても、finally ブロックは必ず実行されるので、「処理が失敗したけど一時ファイルだけ残り続ける」という状態を減らせます。

deleteOnExit に頼りすぎない

File クラスには deleteOnExit() というメソッドがあり、「JVM 終了時に削除してね」とお願いできます。

Path temp = Files.createTempFile("myapp-", ".tmp");
temp.toFile().deleteOnExit();
Java

ただし、これは「プロセスが正常終了したとき」にしか効きません。
強制終了されたり、プロセスが長時間動き続けるサーバーアプリだったりすると、一時ファイルが溜まり続けることになります。

実務では、「基本は使い終わったら自分で消す」「どうしても消し忘れが怖いところだけ補助的に deleteOnExit を使う」くらいの距離感が健全です。


実務で使える「一時ファイル作成ユーティリティ」の最小形

プレフィックス・拡張子・削除責務をまとめる

毎回 createTempFile を直接呼ぶのではなく、「アプリ用の一時ファイル」を作るユーティリティを用意しておくと便利です。

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

public final class TempFiles {

    private TempFiles() {}

    public static Path create(String suffix) throws IOException {
        return Files.createTempFile("myapp-", suffix);
    }

    public static Path createCsv() throws IOException {
        return create(".csv");
    }

    public static void safeDelete(Path path) {
        try {
            Files.deleteIfExists(path);
        } catch (IOException e) {
            // ログにだけ出して、アプリの処理は続行するなど
            System.err.println("一時ファイルの削除に失敗しました: " + path + " : " + e.getMessage());
        }
    }
}
Java

使う側はこう書けます。

Path temp = TempFiles.createCsv();
try {
    // 何かに使う
} finally {
    TempFiles.safeDelete(temp);
}
Java

ここでの重要ポイントは、「“どんな名前でどこに作るか”と“削除時の扱い”を一箇所に集約している」ことです。
プレフィックスを変えたくなったり、削除失敗時のログ出力を変えたくなったりしても、TempFiles だけ直せば済みます。


例題:大きな ZIP を一時ファイルに落としてから展開する

メモリに乗せずに一時ファイルで受ける

例えば、「HTTP で大きな ZIP ファイルをダウンロードして、中身を展開する」という処理を考えます。
これを全部メモリに乗せると、簡単に OutOfMemoryError になります。
そこで、一度一時ファイルに落としてから処理するパターンがよく使われます。

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipInputStream;

public class ZipDownloadJob {

    public void run(InputStream httpBody) throws IOException {
        Path tempZip = TempFiles.create(".zip");
        try {
            // HTTP のボディを一時ファイルに書き出す
            Files.copy(httpBody, tempZip);

            // 一時ファイルから ZIP を展開する
            try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(tempZip))) {
                // エントリごとの処理…
            }
        } finally {
            TempFiles.safeDelete(tempZip);
        }
    }
}
Java

ここで深掘りしたいのは、「一時ファイルを使うことで“メモリ使用量”と“処理の見通し”を両方よくしている」点です。
ストリームをそのまま展開することもできますが、「途中で中身を確認したい」「障害時にファイルを残して調査したい」といった実務的なニーズを考えると、
一時ファイルに落とす設計はかなり現場向きです。


一時ディレクトリを指定して作るパターン

デフォルトではなく「アプリ専用の一時ディレクトリ」を使う

createTempFile には、「どのディレクトリに作るか」を指定できるオーバーロードもあります。

Path baseDir = Path.of("work/tmp");
Files.createDirectories(baseDir);
Path temp = Files.createTempFile(baseDir, "myapp-", ".tmp");
Java

こうすると、OS の共通一時ディレクトリではなく、「アプリ専用の一時ディレクトリ」にファイルをまとめられます。

実務的なメリットは二つあります。

一つは、「掃除しやすい」ことです。
work/tmp 配下だけを定期的に削除するバッチを組めば、「このアプリが残した一時ファイル」だけを安全に消せます。

もう一つは、「他アプリと混ざらない」ことです。
共通の /tmp にいろいろなアプリが一時ファイルを置くと、どれが誰のものか分からなくなります。
専用ディレクトリを切っておけば、「この中はうちのアプリの領域」と割り切れます。

これもユーティリティにしておくと便利です。

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

public final class AppTempFiles {

    private static final Path BASE_DIR = Path.of("work/tmp");

    private AppTempFiles() {}

    public static Path create(String suffix) throws IOException {
        Files.createDirectories(BASE_DIR);
        return Files.createTempFile(BASE_DIR, "myapp-", suffix);
    }
}
Java

一時ファイルとセキュリティの視点

機密情報を一時ファイルに書くときの注意

一時ファイルには、個人情報やトークン、パスワードなど、機密性の高いデータが含まれることがあります。
その場合、「消し忘れ」は単なるディスク問題ではなく、情報漏えいリスクになります。

最低限意識しておきたいのは次の二つです。

一つは、「本当に一時ファイルに書く必要があるか」です。
メモリ上だけで完結できるなら、そのほうが痕跡は残りません。

もう一つは、「消すときは確実に消す設計にする」ことです。
try-finally で削除を保証する、エラー時にも削除を試みる、調査のために残す場合はログに明示的に出す、など、
「残す/残さない」を意識的に選ぶようにすると、後から困りにくくなります。


まとめ:一時ファイル作成ユーティリティで身につけるべき感覚

一時ファイル作成は、「とりあえず tmp に吐く」ではなく、「どこに、どんな名前で、いつまで置き、どう消すか」を設計する行為です。

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

一時ファイルは Files.createTempFile で作り、自分でユニークな名前を考えない。
作るだけでなく、「try-finally で必ず削除する」流れをセットで書く。
プレフィックス・拡張子・削除ポリシーをユーティリティに集約し、アプリ全体で一貫した扱いにする。
大きなデータや ZIP などは、一時ファイルを使うことでメモリを節約しつつ、調査しやすい形にする。
必要に応じて「アプリ専用の一時ディレクトリ」を用意し、掃除や管理をしやすくする。

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