LoginSignup
8
10

More than 5 years have passed since last update.

Jarの中のリソースファイルをどこかにコピーしたい

Posted at

Javaアプリ開発では、クラスファイルやリソースファイルを一つのJarにまとめてパッケージングするのが一般的です。
MavenやGradleプロジェクトであれば、 src/main/resources ディレクトリに置くのが一般的でしょうか。

時々、リソースに含めたファイルをJar外にコピーして使いたいことがあります。
(設定ファイルのテンプレートを外に出したり、配布の都合で同梱していた実行ファイルを実行したいときなど)

resources ディレクトリに配置したファイル hoge.txt のURIは以下のコードで取得できます。

ClassLoader.getSystemResource("hoge.txt").toURI()

URIがわかればあとはこれを所望の場所にコピーすればいいのですが、Jarにした場合とそうでない場合で違いがあってはまりました。

Jarにパッケージングしないでリソースを参照した時のURI

動作試験のために ./gradlew :run とかした場合ですね。

Jarにパッケージングせずに実行した場合は、リソースのURIは file: プロトコルで、リソースファイルの場所を指します。

file:/path/to/project/build/resources/main/hoge.txt

このURIだと File クラスのコンストラクタに渡すことができるので、Kotlinの拡張メソッド File.copyTo() でコピーが可能です。

File(ClassLoader.getSystemResource("hoge.txt").toURI()).copyTo(destination)

しかしJarにパッケージングしてしまうと、URIをFileクラスコンストラクタに渡した途端に例外が出ます。

Jarにパッケージングされたリソースを参照した時のURI

jar: スキームのURIが得られます。

jar:file:/path/to/jar/file/MyApp.jar!/hoge.txt

これをそのままFileのコンストラクタに渡すと java.lang.IllegalArgumentException: URI is not hierarchical が発生します。

コピー元Fileが得られればコピーが楽なのですが、得られないのでそうはいきません。

どうすればいいかと言うと、リソースを開くInputStreamから内容を取り出して、コピー先ファイルに書き込んでしまえば良さそうでした。

自力でコピーメソッドを書くこともできますが、便利なメソッドがすでにあるので使わせてもらいましょう。

JDK付属の Files.copy() (Java 1.7から実装)を使うか、Commons IOFileUtils.copyURLToFile()FileUtils.copyToFile() を使うのがお手軽でしょう。

val srcIs: InputStream = ClassLoader.getSystemResourceAsStream("hoge.txt")
val srcUrl: URL = ClassLoader.getSystemResource("hoge.txt")

// Files.copy() の場合
Files.copy(srcIs, destination.toPath())

// FileUtils.copyURLToFile()の場合
FileUtils.copyURLToFile(srcUrl, destination)

// FileUtils.copyToFile()の場合
FileUtils.copyToFile(srcIs, destination)

とりあえずリソースファイルはURLやURIで扱うのではなく、Streamで扱ったら間違いないようです。

8
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10