【8/27まで】Udemyの人気コースが今なら1,200円から!!

【Java入門】zipファイルを生成・出力する方法

【Java入門】zipファイルを生成・出力する方法

この記事では、zipファイルを作成する方法を解説しています。
1ファイルをzip化する方法、複数ファイルをzip化する方法、フォルダ内のファイルをzip化する方法について解説しています。

ファイルをzip化する方法

以下のように、inputフォルダ直下のテキストファイルをzipファイルに圧縮してみます。

user01@DESKTOP-AQBVPOG MINGW64 /c/workspace/input
$ ls -l
total 1
-rw-r--r-- 1 user01 197609 9 11月 19 15:13 apple.txt

Javaコードは以下のとおりです。

File2Zip.java

public class File2Zip {

	public static void main(String[] args) throws IOException {

		Path filePath = Paths.get("C:\\workspace\\input\\apple.txt");

		Path result = Paths.get("C:\\workspace\\output\\apple.zip");

		//zipファイルを作成
		try(ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(result.toString())))){

			ZipEntry entry = new ZipEntry(filePath.getFileName().toString());
			zos.putNextEntry(entry);
			byte[] data = Files.readAllBytes(filePath);
			zos.write(data);
		};
	}
}

実行後、outputフォルダ配下にapple.zipが作成されていることが確認できます。

user01@DESKTOP-AQBVPOG MINGW64 /c/workspace/output
$ ls -l
total 2
-rw-r--r-- 1 user01 197609 143 11月 19 15:35 apple.zip

解説

Zipファイルの出力先パスについては、outputフォルダまでは存在している必要があります。フォルダが存在していない場合java.io.FileNotFoundExceptionがスローされてしまいます。
apple.zipについては、zipファイル出力時に生成されるので、プログラム実行時点では存在していなくてOKです。

今回は、try-with-resources文を採用しています。そのため、クローズ処理zos.closeEntry();を記述していませんが、処理終了時に自動的にクローズされます。

複数ファイルをzip化する方法

以下のように、inputフォルダ直下に存在する3つのテキストファイルを1つのzipファイルに圧縮してみます。

user01@DESKTOP-AQBVPOG MINGW64 /c/workspace/input
$ ls -l
total 3
-rw-r--r-- 1 user01 197609 9 11月 19 15:13 apple.txt
-rw-r--r-- 1 user01 197609 9 11月 19 15:13 banana.txt
-rw-r--r-- 1 user01 197609 9 11月 19 15:13 lemon.txt

Javaコードは以下のとおりです。

Files2Zip.java

public class Files2Zip {

	public static void main(String[] args) throws IOException  {

		//Zip化したい元ネタとなるテキストファイルが格納されているパス
		Path inputPath = Paths.get("C:\\workspace\\input");

		// 「inputPath」直下のファイルのパスの一覧を取得。
		List<Path> pathList = Files.walk(inputPath)
								.filter(filePath -> filePath != inputPath)
								.collect(Collectors.toList());

		//Zipファイルの出力先パス
		Path result = Paths.get("C:\\workspace\\output\\result.zip");

		//zipファイルを作成
		try(ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(result.toString())))){

			//ファイルの数分、ループする。
			for (Path filePath : pathList){

				//テキストファイルの名前を取得。
				ZipEntry entry = new ZipEntry(filePath.getFileName().toString());
				zos.putNextEntry(entry);

				//テキストファイルの中身をbyte配列で取得し、書き込み。
				byte[] data = Files.readAllBytes(filePath);
				zos.write(data);
			}
		}
	}
}

実行後、outputフォルダ配下にresult.zipが作成されていることが確認できます。

user01@DESKTOP-AQBVPOG MINGW64 /c/workspace/output
$ ls -l
total 1
-rw-r--r-- 1 user01 197609 386 11月 19 15:55 result.zip

解説

まず、「inputPath」直下のファイルのパスの一覧を取得している以下の処理についてです。

List<Path> pathList = Files.walk(inputPath).filter(filePath -> filePath!= inputPath).collect(Collectors.toList());

Files.walkは通常、取得できるパス一覧に自分自身("C:\\workspace\\input")も含まれてしまいます。
ですが、今回取得したいのはzip化したいテキストファイルのパスのみなので、上記のようにフィルタリングすることで自分自身のパスを除外しています。

Zipファイルの出力先パスについては、outputフォルダまでは存在している必要があります。フォルダが存在していない場合java.io.FileNotFoundExceptionがスローされてしまいます。
result.zipについては、zipファイル出力時に生成されるので、プログラム実行時点では存在していなくてOKです。

今回も、try-with-resources文を採用しています。そのため、クローズ処理zos.closeEntry();を記述していませんが、処理終了時に自動的にクローズされます。

複数のフォルダ内に存在するファイルを1つのzipファイルに圧縮する方法

以下のように、inputフォルダ直下に複数のフォルダが存在していてその中にあるファイルを、フォルダ階層を保ったまま1つのzipファイルに圧縮する方法です。

user01@DESKTOP-AQBVPOG MINGW64 /c/workspace/input
$ ls -l -d $(find `pwd`)
drwxr-xr-x 1 user01 197609  0 11月 19 16:44 /c/workspace/input/
-rw-r--r-- 1 user01 197609 12 11月 19 16:44 /c/workspace/input/chocolate.txt
drwxr-xr-x 1 user01 197609  0 11月 19 16:43 /c/workspace/input/fruits/
-rw-r--r-- 1 user01 197609  9 11月 19 15:13 /c/workspace/input/fruits/apple.txt
-rw-r--r-- 1 user01 197609  9 11月 19 15:13 /c/workspace/input/fruits/banana.txt
-rw-r--r-- 1 user01 197609  9 11月 19 15:13 /c/workspace/input/fruits/lemon.txt
drwxr-xr-x 1 user01 197609  0 11月 19 16:43 /c/workspace/input/vegetable/
-rw-r--r-- 1 user01 197609  9 11月 19 16:43 /c/workspace/input/vegetable/tomato.txt

Javaコードは以下のとおりです。

Files2ZipMultiFolder.java

public class Files2ZipMultiFolder {

	public static void main(String[] args) throws IOException  {

		//Zip化したい元ネタとなるテキストファイルが格納されているパス
		Path inputPath = Paths.get("C:\\workspace\\input");

		// 「inputPath」直下のファイルのパスの一覧を取得。
		List<Path> pathList = Files.walk(inputPath)
									.filter(filePath -> !filePath.toFile().isDirectory())
									.collect(Collectors.toList());

		//Zipファイルの出力先パス
		Path result = Paths.get("C:\\workspace\\output\\result.zip");

		//zipファイルを作成
		try(ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(result.toString())))){

			//ファイルの数分、ループする。
			for (Path filePath : pathList){

				//サブパスを取得
				String subPath = filePath.subpath(inputPath.getNameCount(), filePath.getNameCount()).toString();
				ZipEntry entry = new ZipEntry(subPath);
				zos.putNextEntry(entry);

				//テキストファイルの中身をbyte配列で取得し、書き込み。
				byte[] data = Files.readAllBytes(filePath);
				zos.write(data);
			}
		}
	}
}

実行後、outputフォルダ配下にresult.zipが作成されていることが確認できます。

user01@DESKTOP-AQBVPOG MINGW64 /c/workspace/output
$ ls -l
total 1
-rw-r--r-- 1 user01 197609 703 11月 19 18:05 result.zip

これを解凍すると以下のようにフォルダ階層を保ったままファイルが格納されていることを確認できます。

PS C:\workspace\output\result> tree /f
フォルダー パスの一覧:  ボリューム OS
ボリューム シリアル番号は A264-A203 です
C:.
│  chocolate.txt
│
├─fruits
│      apple.txt
│      banana.txt
│      lemon.txt
│
└─vegetable
        tomato.txt

解説

まず、Files.walkのフィルタリング処理についてです。今回は以下のようにしています。

.filter(filePath -> !filePath.toFile().isDirectory())

Files.walkの場合、以下のようなディレクトリだけのパスも取得できてしまいますから、isDirectory()でディレクトリ判定を行うことで、これを除外してファイルのパスだけを取得できるようにしています。

/c/workspace/input/
/c/workspace/input/fruits/
/c/workspace/input/vegetable/

次にサブパスの処理です。以下のように記述しています。

String subPath = filePath.subpath(inputPath.getNameCount(), filePath.getNameCount()).toString();

zipファイル作成時、inputフォルダ配下の各ファイルを、元のフォルダ階層を保ったままzip化することを考えると欲しいパスは以下の黄色部分ということになります。

/c/workspace/input/fruits/apple.txt

/c/workspace/input/fruits/banana.txt

/c/workspace/input/fruits/lemon.txt

/c/workspace/input/vegetable/tomato.txt

/c/workspace/input/chocolate.txt

subpathgetNameCountを使用することで、この黄色部分を取得できるようになります。
これらのメソッドの使い方については以下の記事で解説しています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です