java.nio.file.FileSystem を利用してzipファイルを解凍・圧縮する

# はじめに

java.nio.file.FileSystem を利用してzipファイルを解凍・圧縮する方法を紹介します。

# 解凍

例 : `D:/work/TARGET.zip`を`D:/work/UNZIP`に解凍する

## ソースコード

```Unzip.java
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

void main() {
	try (FileSystem fs = FileSystems.newFileSystem(Path.of("D:/work/TARGET.zip")); 
		 Stream<Path> stream = Files.walk(fs.getPath("/"))) {
		for (Path entry : stream.toList()) {
			// 解凍先のパスを取得する。
			Path destination = Path.of("D:/work/UNZIP").resolve(fs.getPath("/").relativize(entry).toString());
			
			// エントリを格納するフォルダを作成する。
			Files.createDirectories(destination.getParent());
			
			// エントリを解凍する
			Files.copy(entry, destination, StandardCopyOption.REPLACE_EXISTING);
		}
	} catch (IOException e) {
		throw new UncheckedIOException(e);
	}
}
```

## 実行例

`D:/work/TARGET.zip` は以下の通りです。

```
TARGET.zip
├── AAA.txt
└── DIR1
    ├── BBB.txt
    └── DIR2
        └── CCC.txt
```

`Unzip.java`を実行します。

```
java --enable-preview --source 21 Unzip.java
```

`D:/work/UNZIP`に`D:/work/TARGET.zip`が解凍されていることがわかります。

```
C:\>tree /F D:\WORK\UNZIP
フォルダー パスの一覧:  ボリューム ボリューム
ボリューム シリアル番号は 506F-EE01 です
D:\WORK\UNZIP
│  AAA.txt
│
└─DIR1
    │  BBB.txt
    │
    └─DIR2
            CCC.txt
```



# 圧縮

例: `D:/work/TARGET`を`D:/work/TARGET.zip`に圧縮する

## ソースコード

```Zip.java
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

void main() {
	Path start = Path.of("D:/work/TARGET");
	try (FileSystem fs = FileSystems.newFileSystem(Path.of("D:/work/TARGET.zip"), Map.of("create", "true"));
		 Stream<Path> stream = Files.walk(start)) {
		for (Path entry : stream.toList()) {
			if (Files.isSameFile(entry, start)) {
				continue;
			}
			
			// 圧縮先のパスを取得する。
			Path destination = fs.getPath("/").resolve(start.relativize(entry).toString());
			
			// エントリを格納するフォルダを作成する。
			Files.createDirectories(destination.getParent());
			
			// エントリを圧縮する
			Files.copy(entry, destination, StandardCopyOption.REPLACE_EXISTING);
		}
	} catch (IOException e) {
		throw new UncheckedIOException(e);
	}
}
```

## 実行例

`D:/work/TARGET`の内容は以下の通りです。

```
C:\>tree /F D:\WORK\TARGET
フォルダー パスの一覧:  ボリューム ボリューム
ボリューム シリアル番号は 506F-EE01 です
D:\WORK\TARGET
│  AAA.txt
│
└─DIR1
    │  BBB.txt
    │
    └─DIR2
            CCC.txt
```

`Zip.java`を実行します。

```
java --enable-preview --source 21 Zip.java
```

`D:/work/TARGET`の内容が圧縮された`D:/work/TARGET.zip`が作成されました。

```
$ unzip -t /mnt/d/work/TARGET.zip
Archive:  /mnt/d/work/TARGET.zip
    testing: AAA.txt                  OK
    testing: DIR1/                    OK
    testing: DIR1/BBB.txt             OK
    testing: DIR1/DIR2/               OK
    testing: DIR1/DIR2/CCC.txt        OK
No errors detected in compressed data of /mnt/d/work/TARGET.zip.
```

# 環境情報

```
C:\>java -version
openjdk version "21-beta" 2023-09-19
OpenJDK Runtime Environment Temurin-21+35-202309042131 (build 21-beta+35-ea)
OpenJDK 64-Bit Server VM Temurin-21+35-202309042131 (build 21-beta+35-ea, mixed mode, sharing)
```

# 参考

- https://qiita.com/neko_the_shadow/items/6e0504a3304e375b39b0
- https://dev.java/learn/modernio/
- https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemproviderprops.html