Java Tips | I/O・ネットワーク:tar対応

Java Java
スポンサーリンク

業務で「tar対応」ユーティリティが必要になる場面

ZIP は Windows 文化、tar は Unix/Linux 文化——そんなイメージがあります。 業務システムでも、サーバーが Linux だったり、外部システムから .tar.tar.gz が送られてきたりすると、「tar を読めないと仕事にならない」場面が普通に出てきます。

Java 標準ライブラリには tar サポートがないので、 実務では「ライブラリを使って tar を扱うユーティリティ」を用意しておくのが定番です。 ここでは、代表的なライブラリを前提に、「tar対応」を初心者向けに噛み砕いて説明します。

tarとは何か、ZIPとの違いをざっくり押さえる

tarは「圧縮しないアーカイブ」、gzipと組み合わせて使われる

tarは「複数ファイルをひとまとめにするフォーマット」で、 それ自体には圧縮機能がありません。

backup.tar は「ただの束ねたファイルの塊」、 backup.tar.gz は「tarで束ねたものをgzipで圧縮したもの」です。

ZIPは「まとめる+圧縮」を一体でやりますが、 tarは「まとめる」と「圧縮する」を分けて考える——ここが大きな違いです。

代表的なライブラリでの「tar展開」ユーティリティのイメージ

※ここでは、Apache Commons Compress のような「tarを扱えるライブラリ」がある前提で話します。

tarをディレクトリに展開する基本形

tar対応のライブラリは、だいたい「TarArchiveInputStream」のようなクラスを提供します。 ZIPのときと同じく、「エントリを順番に読みながら展開する」スタイルです。

イメージとしては、こんな感じになります。

public class TarExtractUtils {

    public static void extractTar(InputStream tarIn, Path targetDir) throws IOException {
        try (TarArchiveInputStream in = new TarArchiveInputStream(tarIn)) {
            TarArchiveEntry entry;
            while ((entry = in.getNextTarEntry()) != null) {
                if (entry.isDirectory()) {
                    Path dir = targetDir.resolve(entry.getName());
                    Files.createDirectories(dir);
                } else {
                    Path file = targetDir.resolve(entry.getName());
                    Files.createDirectories(file.getParent());

                    try (OutputStream out = Files.newOutputStream(file)) {
                        byte[] buffer = new byte[8192];
                        int len;
                        while ((len = in.read(buffer)) != -1) {
                            out.write(buffer, 0, len);
                        }
                    }
                }
            }
        }
    }
}
Java

ZIP解凍とほぼ同じ構造ですが、クラス名やメソッド名が tar 用になっているだけです。 「エントリを順番に取り出して、ディレクトリなら作る、ファイルなら中身を書き出す」という流れが共通パターンになります。

tar.gz(圧縮付き)を扱うときのストリームの重ね方

gzip解凍 → tar展開、という二段構えのストリーム

.tar.gz を扱うときは、「まず gzip を解凍して、その中身を tar として読む」という二段構えになります。

ストリームの重ね方はこうです。

InputStream fileIn   = Files.newInputStream(pathToTarGz);
InputStream gzipIn   = new GZIPInputStream(fileIn);
TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn);
Java

このように、「外側から順にラップしていく」イメージです。

あとは先ほどの extractTar と同じように、 tarIn.getNextTarEntry() を回しながら展開していきます。

ここで深掘りしたいポイントは、「ストリームの責務を分けて考える」ことです。

gzipは「圧縮を解く」役割、 tarは「束ねられたファイルを一つずつ取り出す」役割。

それぞれのストリームが何をしているかを意識できると、 バグが減るだけでなく、ログや監査の観点でも説明しやすくなります。

パスの扱いとディレクトリトラバーサル対策(tarでも必須)

entry.getName() をそのまま信じない、という姿勢

tarのエントリ名にも、../ を含む相対パスが入っている可能性があります。 ZIPと同じく、「展開先のベースディレクトリからはみ出さないようにする」チェックが重要です。

イメージとしては、次のようなヘルパーを用意します。

private static Path safeResolve(Path baseDir, String entryName) throws IOException {
    Path target = baseDir.resolve(entryName).normalize();
    if (!target.startsWith(baseDir.normalize())) {
        throw new IOException("不正なパスです: " + entryName);
    }
    return target;
}
Java

展開時には必ずこれを通してからファイルパスを決めます。

tarは「Unix文化のツールだから安全」とは限りません。 外部から渡される tar を扱う以上、ZIPと同じくディレクトリトラバーサル対策は必須です。

tar対応ユーティリティの実務的な使いどころ

Linuxサーバーとの連携・バックアップ・ログ収集

Linux系のツールや運用フローでは、 バックアップやログ収集に tar がよく使われます。

例えば、

  • サーバー側で tar コマンドで固めたファイルを、Java側で受け取って展開する
  • 外部システムから .tar.gz で渡されるデータをインポートする
  • Javaで生成したファイル群を tar にして、別の Unix系システムに渡す

といった場面で、「tar対応ユーティリティ」があるかどうかが効いてきます。

まとめ:tar対応ユーティリティで身につけてほしい感覚

tar対応ユーティリティは、「Unix文化のアーカイブ形式を、Javaから自然に扱う」ための橋渡しです。 そこには、次のような感覚が詰まっています。

tarは「まとめる」、gzipは「圧縮する」——役割を分けて理解する。 tar展開は、ZIPと同じく「エントリを順番に読みながらディレクトリとファイルを作る」構造を持つ。 .tar.gz はストリームを重ねて、gzip解凍 → tar展開の二段構えで扱う。 エントリ名を正規化し、ベースディレクトリからはみ出さないようにしてディレクトリトラバーサルを防ぐ。

もしあなたの現場で、 「Linux側から tar で渡されるファイルを手作業で展開している」 「Java側で tar を扱えず、運用が歪んでいる」 という状況があるなら、 この tar対応ユーティリティを軸に、JavaとUnix文化の間のギャップを埋めていくのがいいタイミングです。

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