昨日書いた FileCollection は、実はこの tar and zip の圧縮・展開 を書く為の伏線です。
oboe2uran.hatenablog.com
Apache commons compress を使用して
https://commons.apache.org/proper/commons-compress/
関数型インターフェースで圧縮・展開を用意した。
圧縮対象、複数のケースも考慮して Supplier で対象コレクションを指定するのである。
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.function.Supplier; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; /** * tar and gzip 圧縮/解凍プロセッサ. */ public interface TarGzipProcessor extends Supplier<Collection<FileCollection>>{ /** * tar and gzip 圧縮実行. * @param targzPath 作成する tar and gzip ファイルパス、 *.tar.gz * @return tarエントリ名 Collection */ public default Collection<String> compress(String targzPath){ Collection<String> entries = new ArrayList<>(); String tarpath = targzPath.replaceAll("\\.tar\\.gz$", ".tar"); // tar 生成 try(FileOutputStream out = new FileOutputStream(tarpath); TarArchiveOutputStream taos = new TarArchiveOutputStream(out) ){ get().forEach(fc->{ String prefix = fc.getFile().getParentFile().getAbsolutePath().replaceAll("\\\\", "/"); fc.scan(f->{ try{ if (f.isDirectory()){ TarArchiveEntry entry = new TarArchiveEntry(fc.getFile() , f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, "")); taos.putArchiveEntry(entry); taos.closeArchiveEntry(); entries.add(entry.getName()); return; } TarArchiveEntry entry = new TarArchiveEntry(f, f.getAbsolutePath().replaceAll("\\\\", "/").replaceFirst(prefix, "")); taos.putArchiveEntry(entry); //taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); try(FileInputStream fis = new FileInputStream(f); BufferedInputStream bis = new BufferedInputStream(fis) ){ int size = 0; byte[] buf = new byte[1024]; while((size = bis.read(buf)) > 0){ taos.write(buf, 0, size); } } taos.closeArchiveEntry(); entries.add(entry.getName()); }catch(IOException ex){ throw new RuntimeException(ex); } }); }); }catch(IOException ex){ throw new RuntimeException(ex.getMessage(), ex); } // gzip 生成 try(FileInputStream fis = new FileInputStream(tarpath); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(targzPath); GzipCompressorOutputStream gout = new GzipCompressorOutputStream(fos) ){ int size = 0; byte[] buf = new byte[1024]; while((size = bis.read(buf)) > 0){ gout.write(buf, 0, size); } }catch(IOException ex){ throw new RuntimeException(ex.getMessage(), ex); } new File(tarpath).delete(); return entries; } /** * tar and gzip 展開. * @param targzPath tar and gzip ファイルパス、 *.tar.gz * @param dirPath 展開先ディレクトリPATH * @return 展開された tar エントリ名 */ public static Collection<String> decompress(String targzPath, String dirPath){ Collection<String> entries = new ArrayList<>(); String tarname = targzPath.substring(targzPath.lastIndexOf("/") + 1).replaceAll("\\.gz$", ""); // gzip 解凍 try(FileInputStream fis = new FileInputStream(targzPath); GzipCompressorInputStream gin = new GzipCompressorInputStream(fis); FileOutputStream fos = new FileOutputStream(dirPath + "/" + tarname) ){ int size = 0; byte[] buf = new byte[1024]; while((size = gin.read(buf)) > 0){ fos.write(buf, 0, size); } }catch(IOException ex){ throw new RuntimeException(ex.getMessage(), ex); } // tar展開 try(FileInputStream fis = new FileInputStream(dirPath + "/" + tarname); TarArchiveInputStream tais = new TarArchiveInputStream(fis) ){ ArchiveEntry entry = null; while((entry = tais.getNextEntry()) != null){ File file = new File(dirPath + "/" + entry.getName()); if (entry.isDirectory()){ file.mkdirs(); entries.add(entry.getName()); continue; } if (!file.getParentFile().exists()){ file.getParentFile().mkdirs(); } try(FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos)){ int size = 0; byte[] buf = new byte[1024]; while((size = tais.read(buf)) > 0){ bos.write(buf, 0, size); } entries.add(entry.getName()); } } }catch(IOException ex){ throw new RuntimeException(ex.getMessage(), ex); } new File(dirPath + "/" + tarname).delete(); return entries; } /** * GZIP解凍実行. * @param gzipPath gzip ファイルPATH *.gz * @param dirPath 展開先ディレクトリPATH */ public static void openGz(String gzipPath, String dirPath){ String tarname = gzipPath.substring(gzipPath.lastIndexOf("/") + 1).replaceAll("\\.gz$", ""); try(FileInputStream fis = new FileInputStream(gzipPath); GzipCompressorInputStream gin = new GzipCompressorInputStream(fis); FileOutputStream fos = new FileOutputStream(dirPath + "/" + tarname) ){ int size = 0; byte[] buf = new byte[1024]; while((size = gin.read(buf)) > 0){ fos.write(buf, 0, size); } }catch(IOException ex){ throw new RuntimeException(ex.getMessage(), ex); } } }
Windowsでも使えるように replaceAll が入ってしまった。。
1つのディレクトリを指定し、その配下を tar.gz にする例、
FileCollection fileCollection = FileCollection.of("/home/aaa"); TarGzipProcessor processor = ()->Arrays.asList(fileCollection); processor.compress("/home/aaa.tar.gz");
複数対象にした圧縮。。。"/home/aaa/" ディレクトリ配下を対象にする場合、
= tarエントリに、親ディクトリ "aaa" を含めさせたくない場合である。
List<FileCollection> fileCollections = Arrays.stream(new File("/home/aaa").listFiles()) .map(e->FileCollection.of(e.getAbsolutePath())) .collect(Collectors.toList()); TarGzipProcessor processor = ()->fileCollections; processor.compress(targzipPath);
いきなり、Supplier を使うから、こんな書き方
TarGzipProcessor processor = ()->fileCollections;
で、古くから Java に親しんできた者には、まるで Java のコードでないように一瞬見えてしまいますが、
これが、なんとも簡潔であると気づく。。