はじめに
実行可能なJARファイル(Java 8)で src/main/resources
以下に格納したディレクトリを外部のディレクトリにコピーしたかったので方法を調べました。
対象がファイルであれば java.lang.ClassLoader.getResourceAsStream()
で InputStream
を取得して処理すれば良いです。
やりたいこと
src/main/resources/my_resource_dir
というパスのディレクトリをコピーしたいとします。
src ├── main │ ├── resources │ │ └── my_resource_dir │ │ └── foo │ │ └── bar │ │ └── ...
うまくいかない方法
IDEから実行した場合には以下のコードは期待通りに動作します。
しかし、JARファイルから実行すると IllegalArgumentException
例外がスローされます。これはファイルシステム上にコピーするリソースが存在しないためです。JARは単一のファイルなので、ファイルの一部である my_resource_dir
はストリームとして扱う必要があります。
String srcDirName = "my_resource_dir"; // コピーしたいリソース File destDir = new File("/path/to/dest"); // コピー先のディレクトリ File resource = new File(MyClass.class.getClassLoader().getResource(srcDirName).toURI()); FileUtils.copyDirectory(resource, destDir);
$ java -jar your-jar-with-dependencies.jar java.lang.IllegalArgumentException: URI is not hierarchical
うまくいく方法
リンク先の copyResourcesToDirectory
に答えがありました。
実行環境の判定(JARか否か)は以下のコードで実現できます。
以下のリンク先を参考にしました。
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()); if (jarFile.isFile()) { // JARの場合 } else { // IDEの場合 }
これで実行方法によらず resources
以下に管理するディレクトリをコピーするコードが出来上がりました。
srcDirName
に与えたディレクトリ名 + /
から始まるJARのエントリ(ファイルのみ)から InputStream
を取得し、一つずつコピーしていってます。
String srcDirName = "my_resource_dir"; // コピーしたいリソース File destDir = new File("/path/to/dest"); // コピー先のディレクトリ final File jarFile = new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath()); if (jarFile.isFile()) { // JARで実行する場合 final JarFile jar = new JarFile(jarFile); for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements();) { JarEntry entry = entries.nextElement(); if (entry.getName().startsWith(srcDirName + "/") && !entry.isDirectory()) { File dest = new File(destDir, entry.getName().substring(srcDirName.length() + 1)); File parent = dest.getParentFile(); if (parent != null) { parent.mkdirs(); } FileOutputStream out = new FileOutputStream(dest); InputStream in = jar.getInputStream(entry); try { byte[] buffer = new byte[8 * 1024]; int s = 0; while ((s = in.read(buffer)) > 0) { out.write(buffer, 0, s); } } finally { in.close(); out.close(); } } } jar.close(); } else { // IDEで実行する場合 final File resource = new File(MyClass.class.getClassLoader().getResource(srcDirName).toURI()); FileUtils.copyDirectory(resource, destDir); }