ファイルをInputStreamとしてロードするさまざまな方法


216

違いは何ですか:

InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName)

そして

InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)

そして

InputStream is = this.getClass().getResourceAsStream(fileName)

どれが他のどれよりも適切に使用するのはいつですか?

読み取りたいファイルは、ファイルを読み取るクラスとしてクラスパスにあります。私のクラスとファイルは同じjarにあり、EARファイルにパッケージ化され、WebSphere 6.1にデプロイされています。

回答:


289

fileNameあなたが通過している方法がどのように解釈されるかに関して微妙な違いがあります。基本的に、2つの異なる方法がClassLoader.getResourceAsStream()ありClass.getResourceAsStream()ます。これらの2つの方法では、リソースの位置が異なります。

ではClass.getResourceAsStream(path)、パスは、呼び出し元のクラスのパッケージのローカルパスとして解釈されます。たとえば、を呼び出すと、String.getResourceAsStream("myfile.txt")クラスパスで次の場所にあるファイルが検索されます"java/lang/myfile.txt"。パスがで始まる場合、/それは絶対パスと見なされ、クラスパスのルートから検索を開始します。したがって、呼び出しString.getResourceAsStream("/myfile.txt")はクラスパスの次の場所を調べます./myfile.txt

ClassLoader.getResourceAsStream(path)すべてのパスを絶対パスと見なします。したがって、を呼び出すString.getClassLoader().getResourceAsStream("myfile.txt")String.getClassLoader().getResourceAsStream("/myfile.txt")、次の場所でクラスパス内のファイルが検索されます./myfile.txt

この投稿で場所について言及するときはいつでも、リソースのロード元のクラスやクラスローダーに応じて、ファイルシステム自体の場所、または対応するjarファイル内の場所になる可能性があります。

あなたのケースでは、アプリケーションサーバーからクラスをロードしているので、Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)ではなくを使用する必要がありますthis.getClass().getClassLoader().getResourceAsStream(fileName)this.getClass().getResourceAsStream()も動作します。

この特定の問題に関する詳細情報については、この記事をお読みください。


Tomcat 7以下のユーザーへの警告

この質問への回答の1つに、Tomcat 7では私の説明が正しくないように見えるとあります。なぜそうなのかを調べてみました。

そこで、TomcatのWebAppClassLoaderいくつかのバージョンのTomcat のソースコードを確認しました。findResource(String name)(要求されたリソースへのURLの生成に最終的に責任がある)の実装は、Tomcat 6とTomcat 7で実質的に同じですが、Tomcat 8では異なります。

バージョン6および7では、実装はリソース名を正規化しようとしません。つまり、これらのバージョンでclassLoader.getResourceAsStream("/resource.txt")は、classLoader.getResourceAsStream("resource.txt")イベントと同じ結果が生成されない可能性があります(Javadocで指定されているため)。[ソースコード]

ただし、バージョン8では、リソース名は正規化されており、リソース名の絶対バージョンが使用されるバージョンであることが保証されています。したがって、Tomcat 8では、上記の2つの呼び出しは常に同じ結果を返すはずです。[ソースコード]

その結果、あなたが使用している場合、余分な注意する必要がありますClassLoader.getResourceAsStream()またはClass.getResourceAsStream()8より前のTomcatのバージョンにそして、あなたもいることを心に留めておく必要がありclass.getResourceAsStream("/resource.txt")、実際の呼び出しはclassLoader.getResourceAsStream("resource.txt")(先頭が/取り除かれます)。


2
とはgetClass().getResourceAsStream("/myfile.txt")動作が異なると確信していgetClassLoader().getResourceAsStream("/myfile.txt")ます。
ブライアンゴードン

@BrianGordon:彼らは違ったふるまいをしません。実際、Class.getResourceAsStream(String)の javadoc は、「このメソッドはこのオブジェクトのクラスローダーに委譲します。」と言ってから、相対パスを絶対パスに変換してから、クラスローダー。
LordOfThePigs 2013年

@LordOfThePigs実際のソースを見てください。Class.getResourceAsStreamは、絶対パスを指定した場合、先頭のスラッシュを取り除きます。
ブライアンゴードン

4
@BrianGordon:後者は、先頭のスラッシュで始まるかどうかに関係なく、すべてのパスを絶対パスとして解釈するため、ClassLoader.getResourceAsStream()とまったく同じように動作します。したがって、パスが絶対パスである限り、どちらの方法も同じように動作します。パスが相対パスの場合、動作は異なります。
LordOfThePigs 2013年

を見つけることができませんgetClassLoader()でしたString。それは間違いですか、拡張機能が必要ですか?
AaA

21

MyClass.class.getClassLoader().getResourceAsStream(path)コードに関連付けられたリソースを読み込むために使用します。MyClass.class.getResourceAsStream(path)ショートカットとして、およびクラスのパッケージ内にパッケージ化されたリソースに使用します。

Thread.currentThread().getContextClassLoader().getResourceAsStream(path)呼び出しコードに厳密にバインドされていない、クライアントコードの一部であるリソースを取得するために使用します。スレッドコンテキストクラスローダーが何かを指している可能性があるため、これには注意が必要です。


6

プレーンオールドJava 7上のプレーンオールドJavaと他の依存関係は違いを示していません...

私が入れfile.txtc:\temp\と私は入れてc:\temp\クラスパス上に。

2つの呼び出しに違いがあるケースは1つだけです。

class J {

 public static void main(String[] a) {
    // as "absolute"

    // ok   
    System.err.println(J.class.getResourceAsStream("/file.txt") != null); 

    // pop            
    System.err.println(J.class.getClassLoader().getResourceAsStream("/file.txt") != null); 

    // as relative

    // ok
    System.err.println(J.class.getResourceAsStream("./file.txt") != null); 

    // ok
    System.err.println(J.class.getClassLoader().getResourceAsStream("./file.txt") != null); 

    // no path

    // ok
    System.err.println(J.class.getResourceAsStream("file.txt") != null); 

   // ok
   System.err.println(J.class.getClassLoader().getResourceAsStream("file.txt") != null); 
  }
}

非常に感謝して、私は 'J.class.getResourceAsStream( "file.txt")'だけを機能させました
abbasalim

3

ここにあるこれらすべての回答、およびこの質問の回答は、「/ foo / bar.properties」のような絶対URLのロードがclass.getResourceAsStream(String)およびによって同じように処理されたことを示唆していますclass.getClassLoader().getResourceAsStream(String)。これは、少なくとも私のTomcat構成/バージョン(現在は7.0.40)には当てはまりません。

MyClass.class.getResourceAsStream("/foo/bar.properties"); // works!  
MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work!

申し訳ありませんが、満足できる説明はまったくありませんが、Tomcatがクラスローダーでダーティトリックと彼の黒魔術を実行し、違いを引き起こしていると思います。昔はいつも使っclass.getResourceAsStream(String)ていて、問題はありませんでした。

PS:私もここにこれを投稿しまし


たぶんtomcatは仕様を尊重しないことを決定し、渡さClassLoader.getResourceAsStream()れたすべてのパスを絶対パスとして扱いませんか?上記のコメントで述べたように、Class.getResourceAsStream実際にはgetClassLoader()。getResourceAsStream`を呼び出しますが、先頭のスラッシュは取り除かれているため、これはもっともらしいことです。
LordOfThePigs 2013

Java SEののソースコードをチェックインした後、私は答えを保持すると思う:どちらClass.getResourceAsStream()ClassLoader.getResourceAsStream()最終的には、呼び出し元に終わるClassLoader.findResource()デフォルト実装は空で保護された方法であるが、そのjavadocを明示的にクラスローダの実装がどこに指定するには、このメソッドをオーバーライドする必要があります」と述べリソースを見つける」。Tomcatのこの特定の方法の実装には欠陥があるのではないかと思います。
LordOfThePigs 2014

私はまたの実装と比較してきたWebAppClassLoader.findResource(String name)のTomcat 7のものとTomcatの8を、そして、重要な違いがあることが表示されます。Tomcat 8は、リソース名が/含まれていない場合は先頭を追加してリソース名を明示的に正規化し、すべての名前を絶対的なものにします。Tomcat 7ではサポートされていません。それは明らかにTomcat 7のバグです
LordOfThePigs '06 / 07/06

私は私の回答にそのことについての段落を追加しました。
LordOfThePigs 2014

0

いくつかの方法でファイルをロードしても成功しなかった後、を使用できることを思い出しましたFileInputStream

InputStream is = new FileInputStream("file.txt");

これは、ファイルをに読み込むもう1つの方法でInputStream、現在実行中のフォルダからファイルを読み取ります。


これはファイルではなく、リソースです。正解ではありません。
ローン侯爵

1
@EJP私はこのSOの答えに終わり、ファイルとリソースの違いを知らずにファイルをロードする方法を探しています。他の人の役に立つかもしれないので、私の答えを削除するつもりはありません。
Antónioのアルメイダ

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