Java7 Zip

Java7からnioにFilesが追加されたりと諸々ファイル周りのAPIが強化されてますね。
個人的には、Zipをライブラリに頼らず、Javaだけでサクッとやりたいと思ってました。
Zip File System Provider

まぁドキュメントやらgoogle先生に助けてもらって実装出来たんですけど、
圧縮は問題なさそうですね。
SimpleFileVisitorでディレクトリを探索して、Zipに追加って感じで。
相対パスをちゃんと考えないといけないので、
変数名を分かりやすくしとかないと若干混乱します。

で、解凍なんですが、圧縮と同じような考えでいけます。
解凍のほうがコードは短くすみます。
ただ、どうにも分からなかったのが、
手動で圧縮したファイルやJavaで圧縮したZipは問題なく解凍出来たのですが、
HttpClientで取得したファイルの解凍が出来ませんでした。

InputStream in = responce.getEntityInputStream();
byte[] bytes = ByteStreams.toByteArray(in); // guava
File file = new File("C:/Users/nosa/AppData/Local/Temp/temp.zip");
Files.write(file.toPath(), bytes, StandardOpenOption.CREATE);

これを解凍したかったんですが、このZipをFileSystemに食わすと

java.util.zip.ZipError: zip END header not found
	at com.sun.nio.zipfs.ZipFileSystem.zerror(ZipFileSystem.java:1605)
	at com.sun.nio.zipfs.ZipFileSystem.findEND(ZipFileSystem.java:1021)
	at com.sun.nio.zipfs.ZipFileSystem.initCEN(ZipFileSystem.java:1030)
	at com.sun.nio.zipfs.ZipFileSystem.<init>(ZipFileSystem.java:130)
	at com.sun.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:117)
	at java.nio.file.FileSystems.newFileSystem(FileSystems.java:322)
	at java.nio.file.FileSystems.newFileSystem(FileSystems.java:272)

が発生します。
昔ながらの方法(ZipFile,ZipEntry)の方法だと解凍出来ました。(もちろん手動でも解凍出来る)
また、zip4jでも解凍出来ました。

圧縮

        File zipDir = new File(dirPath);
        final Path zipPath = zipDir.toPath();
        // 圧縮
        URI zipUri = zipFile.toURI();
        URI uri = new URI("jar:" + zipUri.getScheme(), zipUri.getPath(), null);
        Map<String, String> env = new HashMap<>();
        env.put("create", "true");
        env.put("encoding", "UTF-8");
        try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
            for (File srcFile : zipDir.listFiles()) {
                if (srcFile.isDirectory()) {
                    // ディレクトリ
                    Files.walkFileTree(srcFile.toPath(), new SimpleFileVisitor<Path>() {
                        @Override
                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                                throws IOException {
                            // zipするディレクトリでのファイルの相対パス
                            Path targetPath = zipPath.relativize(file);
                            Files.copy(file, zipfs.getPath(targetPath.toString()),
                                    StandardCopyOption.REPLACE_EXISTING);
                            return FileVisitResult.CONTINUE;
                        }

                        @Override
                        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                                throws IOException {
                            // zipするディレクトリでのディレクトリの相対パス
                            Path targetPath = zipPath.relativize(dir);
                            Files.createDirectory(zipfs.getPath(targetPath.toString()));
                            return FileVisitResult.CONTINUE;
                        }
                    });
                } else {
                    // ファイル
                    Path dest = zipfs.getPath(srcFile.getName());
                    Files.copy(srcFile.toPath(), dest, StandardCopyOption.REPLACE_EXISTING);
                }
            }
        }

解凍

        // 解凍
        URI zipUri = zipFile.toURI();
        URI uri = new URI("jar:" + zipUri.getScheme(), zipUri.getPath(), null);
        Map<String, String> env = new HashMap<>();
        env.put("create", "false");
        env.put("encoding", "UTF-8");
        try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
            final Path zipRoot = zipfs.getPath("/");
            Files.walkFileTree(zipRoot, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                        throws IOException {
                    Path targetPath = Paths.get(toDir.toString(), file.toString());
                    Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                        throws IOException {
                    Path targetPath = Paths.get(toDir.toString(), dir.toString());
                    Files.createDirectory(targetPath);
                    return FileVisitResult.CONTINUE;
                }
            });
        }

うーん。SimpleFileVisitorの仕様は好きなんですけど原因分からず、zip4jに逃げようかと...。
Zipフォーマットを理解してからもう一度調べてみよう。
Zipが壊れてて手でも解凍出来ない、ZipFileでも解凍出来ないとかであれば、
良かったんですけど。


下記、大いに参考にさせて頂きました。
JavaSE ZIP圧縮/解凍 Java7 - @//メモ