Java JARファイル内のリソースへのパスを取得する方法


166

リソースへのパスを取得しようとしていますが、うまくいきませんでした。

これは(IDEとJARの両方で)機能しますが、この方法ではファイルへのパスを取得できず、ファイルの内容のみを取得できます。

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

私がこれをすると:

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

結果は次のとおりです。 java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

リソースファイルへのパスを取得する方法はありますか?


1
はい。私は両方で作業したいクラス、外部のフォルダー(構成ファイルのパラメーターを変更したい場合)とユーザーに実装構成ファイルを非表示にするJAR(配布可能ファイルなど)を持っていますすべての人へのJAR)。
no_ripcord 2009年

1
したがって、クラスはファイル(構成ファイル)へのPATHを受け取るだけです。
no_ripcord 2009年

3
次に、おそらくそのクラスに入力ストリームを処理させる必要があります。入力ストリームはどちらのソースからでも取得できます。
カールマナスター、2009年

1
はい、知っています。しかし、それは他の方法でより明確でクリーンだったでしょう。とにかくthx。
no_ripcord 2009年

1
この質問でオプション#4のようなことをしようとしていますか?stackoverflow.com/questions/775389/...
エリクソン

回答:


71

これは意図的なものです。「ファイル」の内容がファイルとして利用できない場合があります。JARファイルやその他の種類のリソースの一部である可能性があるクラスやリソースを扱っていることを思い出してください。クラスローダーはリソースへのファイルハンドルを提供する必要はありません。たとえば、jarファイルがファイルシステムの個々のファイルに展開されていない可能性があります。

java.io.Fileが絶対に必要な場合、java.io.Fileを取得することでできることはすべて、ストリームを一時ファイルにコピーして同じことを行うことで実行できます。


6
リソースを開いて開くときに「rsrc:」を追加できます。new File( "rsrc:filename.txt")のように、jarのルート内にパックされたfilename.txtをロードします
gipsh

63

リソースをロードするときは、次の違いに注意してください。

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

そして

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

おそらく、この混乱がリソースのロード時に問題のほとんどを引き起こしています。


また、画像をロードしているときは使いやすいですgetResourceAsStream()

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

JARアーカイブから(非イメージ)ファイルを本当にロードする必要がある場合は、次のことを試してみてください。

File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile("tempfile", ".tmp");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}

if (file != null && !file.exists()) {
    throw new RuntimeException("Error: File " + file + " not found!");
}

3
+1これでうまくいきました。bin`/com/myorg/filename.ext 'パスを使用する前に、読み込むファイルをフォルダに入れ、リソースをロードするクラスのディレクトリに移動していることを確認してください。
rayryeng 2014

+1これも機能します...このアプローチに関連するセキュリティリスクが存在する可能性があることは理解しているので、アプリケーションの所有者はそれを認識する必要があります。
LucasA 2014年

このステートメントを明確にできますか?「getResourceAsStream()でリソースをロードする方が常に良い」?これはどのように問題の解決策を提供できますか?
Luca S.

@LucaS。これは画像の場合のみを対象としたものでした。申し訳ありませんが、あまり明確ではありませんでした。このアプローチはプラットフォームに依存しないものである必要がありますが、少しハッキーな方法です。
トゥーンバルト2015年

@LucasAあなたが言及したリスクを明確にしていただけますか?
ZX9 2016年

25

一行の答えは-

String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()

基本的にgetResourceメソッドはURLを提供します。このURLから、次の呼び出しによりパスを抽出できますtoExternalForm()

参照:

getResource()toExternalForm()


7
私の環境(IntelliJ)から実行している間、これはすべてのケースで機能する単純なファイルURLを生成します。ただし、jar自体から実行すると、jar:file:/path/to/jar/jarname.jar!/file_in_jar.mp4のようなURIが表示されます。すべてがjarで始まるURIを利用できるわけではありません。JavaFXメディアの適例。
Noah Ternullo 2014

1
私はこの答えが一番好きです。確かに、ほとんどの場合、jarファイル内にあるリソースにInputStreamを取得するほうが望ましい場合がありますが、何らかの理由で本当にパスが必要な場合は、これが機能します。サードパーティのオブジェクトに渡すパスが必要でした。ほんとありがと!
マリオ

1
上記のソリューションはIDEでは機能せず、Intellijitではfile:/pathに追加されます。これはjar では機能しますがIDEでは機能しません
Tayab Hussain

あなたの返答は、私が今から少なくとも2日間処理しようとしていた問題を解決しました。TYVM !!
ジョナサン

12

私はこの問題をいじくり回していましたが、実際には解決策が見つからなかったため、奇妙なことに十分です!特にWindowsの[スタート]メニューからJAR(またはプログラム)を実行する場合は、作業ディレクトリがJARのディレクトリではないことがよくあります。だからここに私がやったことがあり、それはJARのように機能するのと同様に、JARの外部から実行される.classファイルでも機能します。(Windows 7でのみテストしました。)

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}

8

netclient.pJARファイル内にある場合、そのファイルは他のファイル内にあるため、パスはありません。その場合、あなたが持つことができる最良の道は本当にfile:/path/to/jarfile/bot.jar!/config/netclient.pです。


この形式のURL(... bot.jar!/ config / ...)をURIに変換しようとすると、パスが階層的でないと表示されます。
gEdringer

7

jarファイル内のパスを理解する必要があります。
単にそれを相対的に参照してください。したがって、ファイル(myfile.txt)がある場合は、\src\main\resourcesディレクトリ(mavenスタイル)の下のfoo.jarにあります。あなたはそれを次のように参照します:

src/main/resources/myfile.txt

を使用してjarをダンプするjar -tvf myjar.jar と、jarファイル内の出力と相対パスが表示され、FORWARD SLASHESを使用します。


確かに、Windowsでもスラッシュを使用する必要があります。つまり、は使用できませんFile.separator
str

3

私の場合、Pathの代わりにURLオブジェクトを使用しました。

ファイル

File file = new File("my_path");
URL url = file.toURI().toURL();

クラスローダーを使用したクラスパス内のリソース

URL url = MyClass.class.getClassLoader().getResource("resource_name")

コンテンツを読み取る必要がある場合は、次のコードを使用できます。

InputStream stream = url.openStream();

また、InputStreamを使用してコンテンツにアクセスできます。


3

これは、ストリームがフラッシュされ、jarリソースからの一時ファイルのコンテンツの不完全なコピーを回避し、一意の一時ファイル名を持つように閉じる、ユーザーTombartの同じコードです。

File file = null;
String resource = "/view/Trial_main.html" ;
URL res = getClass().getResource(resource);
if (res.toString().startsWith("jar:")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile(new Date().getTime()+"", ".html");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.flush();
        out.close();
        input.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}
         

2

ファイルはファイルシステム内のファイルを抽象化したものであり、ファイルシステムはJARの内容について何も認識していません。

URIで試してください。あなたの目的に役立つ可能性のあるjar://プロトコルがあると思います。



1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

これを取得getSystemResourceAsStreamするのが最良のオプションです。ファイルやURLではなくinputstreamを取得することにより、JARファイルでスタンドアロンとして動作します。



0

jarファイルの場合、リソースは絶対にパッケージ階層に配置されます(ファイルシステム階層ではありません)。したがって、「./ default.conf」という名前のリソースをロードするクラスcom.example.Sweetがある場合、リソースの名前は「/com/example/default.conf」として指定されます。

しかし、それがjarファイルにある場合は、ファイルではありません...


0

jarのressourcesフォルダー(java / main / resources)内にファイルを追加し(imports.xmlという名前のxmlファイルを追加したと想定)、その後、以下のResourceLoaderようなSpringを使用するかどうかを注入します

@Autowired
private ResourceLoader resourceLoader;

ツアー関数の内部で、ファイルをロードするために次のコードを記述します。

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}

0

たぶん、この方法は迅速な解決に使用できます。

public class TestUtility
{ 
    public static File getInternalResource(String relativePath)
    {
        File resourceFile = null;
        URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
        String codeLocation = location.toString();
        try{
            if (codeLocation.endsWith(".jar"){
                //Call from jar
                Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
                resourceFile = path.toFile();
            }else{
                //Call from IDE
                resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
            }
        }catch(URISyntaxException ex){
            ex.printStackTrace();
        }
        return resourceFile;
    }
}

いくつかのコンテキストを省略していますか?これは私に与える:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
アレンルーチェ

私は答えを編集して、ソフトウェアで使用した方法全体を書いた
gbii

0

コードに従ってください!

/ src / main / resources / file

streamToFile(getClass().getClassLoader().getResourceAsStream("file"))

public static File streamToFile(InputStream in) {
    if (in == null) {
        return null;
    }

    try {
        File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        f.deleteOnExit();

        FileOutputStream out = new FileOutputStream(f);
        byte[] buffer = new byte[1024];

        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        return f;
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        return null;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.