jar内からリソースファイルを読み取る


241

私のjar内から次のようにリソースを読みたいのですが:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

Eclipseで実行すると問題なく動作しますが、jarにエクスポートすると、IllegalArgumentExceptionが発生します。

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

理由は本当にわかりませんが、いくつかのテストで変更した場合に見つかりました

file = new File(getClass().getResource("/file.txt").toURI());

file = new File(getClass().getResource("/folder/file.txt").toURI());

次に、逆に機能します(jarでは機能しますが、Eclipseでは機能しません)。

私はEclipseを使用しており、ファイルを含むフォルダーはクラスフォルダーにあります。


jar内のディレクトリからファイルの番号を含むファイルを読み取る場合は、Stackoverflow-Link
Michael Hegner

1
元の質問が春に関するものだったかどうかはわかりません。前のコメントのリンクは、別の質問からのSpring固有の回答を参照しています。私はgetResourceAsStreamこの問題に対するより簡単で移植性の高い解決策であると私は信じています。
Drew MacInnis 2016

回答:


397

リソースをファイルとしてアドレス指定しようとするのではなく、getResourceAsStreamを介してリソースのInputStreamを返すようにクラスローダーに要求するだけです。

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

限り、file.txtリソースがクラスパス上で利用可能であるし、このアプローチは、かどうかにかかわらず、同じように動作しますfile.txtリソースをしているclasses/ディレクトリまたは内側jar

これURI is not hierarchicalは、jarファイル内のリソースのURIが次のようになるために発生しますfile:/example.jar!/file.txt。あなたは内のエントリを読み取ることができませんjarzipそれは昔ながらのだったようにファイル)ファイル

これは、以下の回答でよく説明されています。


3
ありがとう、これは非常に役に立ち、コードは完全に機能しますが、問題が1つあります。InputStream存在するかどうか(などFile.exists())を確認して、ゲームがデフォルトファイルを使用するかどうかを判断できるようにする必要があります。ありがとう。
PrinceCJC、2013

1
ああ、ところでgetClass().getResource("**/folder**/file.txt")それが機能した理由は、そのフォルダーがjarと同じディレクトリにあったからです:)。
PrinceCJC、2013

5
getResourceAsStreamリソースが存在しない場合はnullを返すため、これは「存在」テストになります。
Drew MacInnis 2013

1
ところで、タイプミスがあります:それはBufferredReaderではなくBufferedReaderである必要があります(後で追加の「r」に注意してください)
mailmindlin

2
そしてもちろん... inputStreamとBufferedReaderを閉じることをお忘れなく
Noremac

26

jar内のファイルにアクセスするには、2つのオプションがあります。

  • ファイルをパッケージ名と一致するディレクトリ構造に配置し(.jarファイルを抽出した後、.classファイルと同じディレクトリにある必要があります)、次を使用してアクセスします getClass().getResourceAsStream("file.txt")

  • ファイルをルートに配置し(.jarファイルを抽出した後、ルートにある必要があります)、次を使用してアクセスします Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

jarがプラグインとして使用されている場合、最初のオプションは機能しない可能性があります。


このすばらしい回答をありがとうございました... 2番目のオプションのもう1つの利点は、getClass()がインスタンスからのみ機能し、静的メソッドからは機能しないため、メインメソッドからも機能することです。
Dan Ortega

6

以前にこの問題があり、ロードのフォールバック方法を作成しました。基本的に、最初の方法は.jarファイル内で動作し、2番目の方法はEclipseまたは他のIDE内で動作します。

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}

3

これまで(2017年12月)、これは、IDEの内部と外部の両方で機能する唯一の解決策です。

PathMatchingResourcePatternResolverを使用する

注:スプリングブートでも機能します

この例では、src / main / resources / my_folderにあるいくつかのファイルを読み取っています。

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}

3

問題は、特定のサードパーティライブラリが入力ストリームではなくファイルパス名を必要とすることです。ほとんどの回答はこの問題に対処していません。

この場合、1つの回避策は、リソースの内容を一時ファイルにコピーすることです。次の例では、jUnitを使用していTemporaryFolderます。

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }

これはたくさんの光沢があります。文書化していただきありがとうございます。JUnitなしで他にどんなオプションがあるのか​​知りたいです。
Alex Moore-Niemi

0

ファイルとして読みたい場合は、同様の解決策がまだあると思います:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());

4
URL.getFile()、URLをファイル名に変換しません。ホストの後のURLの部分を返し、パーセントエンコーディングはすべてそのままなので、パスに非ASCII文字またはURLで許可されていないASCII文字(スペースを含む)が含まれている場合、結果は既存のファイル名にはなりません、URLがURLであってもfile:
VGR 2016年

48
プログラムがjarにビルドされると、これは内部では機能しません
Akshay Kasar 2017

1
最初に文字列に変換してローカルに保存しない限り、jarからは機能しません。
smoosh911 2018

0

正しいセパレータで作業していることを確認してください。/相対パスのすべてをに置き換えましたFile.separator。これはIDEでは問題なく機能しましたが、ビルドJARでは機能しませんでした。


0

これはJavaでも動作するはずです。私が使用する次のコードは、kotlinを使用しています。

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')

0

修正を見つけました

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

「Main」をコーディングしたJavaクラスに置き換えます。「path」をjarファイル内のパスに置き換えます。

たとえば、パッケージcom.issac.stateにState1.txtを配置した場合、LinuxまたはMacを実行している場合は、パスを「/ com / issac / state / State1」と入力します。Windowsを実行している場合は、パスを「\ com \ issac \ state \ State1」と入力します。File not found例外が発生しない限り、ファイルに.txt拡張子を追加しないでください。


-1

クラスパスからROOTパスとして読み取るクラスローダーを使用できます(最初に「/」なし)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

-1

Springを使用している場合は、次の方法でsrc / main / resourcesからファイルを読み取ることができます。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }

1
SOへようこそ。これは質問に対する答えを提供しません。十分な評判られると、どの投稿にもコメントできるようになります。また、代わりに何ができるかを確認してください。
2017年

これにより、ファイル内の改行が削除されます。
elad.chen

-1

何らかの理由でclassLoader.getResource()、私はからのクラスローダを取得WildFly 14にWebアプリケーションを展開するとき常にnullを返したgetClass().getClassLoader()か、Thread.currentThread().getContextClassLoader()nullを返します。

getClass().getClassLoader() APIドキュメントは言う、

「クラスのクラスローダーを返します。一部の実装では、ブートストラップクラスローダーを表すためにnullを使用する場合があります。このクラスがブートストラップクラスローダーによって読み込まれた場合、このメソッドはこのような実装でnullを返します。」

WildFlyを使用していて、Webアプリケーションでこれを試してみてください

request.getServletContext().getResource()リソースのURLを返しました。ここでのリクエストはServletRequestのオブジェクトです。


-2

以下のコードはSpring boot(kotlin)で動作します:

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.