getResourceAsStream은 null을 반환


183

Java 프로젝트의 컴파일 된 JAR의 패키지 내에서 텍스트 파일을로드하고 있습니다. 관련 디렉토리 구조는 다음과 같습니다.

/src/initialization/Lifepaths.txt

내 코드는를 Class::getResourceAsStream반환하기 위해 호출 하여 파일을로드 합니다 InputStream.

public class Lifepaths {
    public static void execute() {
        System.out.println(Lifepaths.class.getClass().
            getResourceAsStream("/initialization/Lifepaths.txt"));
    }

    private Lifepaths() {}

    //This is temporary; will eventually be called from outside
    public static void main(String[] args) {execute();}
}

인쇄물은 null내가 무엇을 사용하든 항상 인쇄 합니다. 위의 방법이 작동하지 않는 이유를 잘 모르겠으므로 시도해 보았습니다.

  • "/src/initialization/Lifepaths.txt"
  • "initialization/Lifepaths.txt"
  • "Lifepaths.txt"

이 둘 중 어느 것도 작동하지 않습니다. 나는 한 읽고 많은 질문을 지금까지 주제에,하지만 그들 중 누구도 도움이 없었다 - 보통, 그들은 단지 이미하고 있어요 루트 경로를 사용하여로드 파일을 말한다. 또는 현재 디렉토리에서 파일을로드 (로드 만하면됩니다 filename). 내도 시도했습니다. 파일이 적절한 이름으로 적절한 위치에있는 JAR로 컴파일됩니다.

이 문제를 어떻게 해결합니까?


3
당신이 정말 것을 확인해 봤어 이다 jar 파일에? 파일 케이스를 확인 했습니까?
Jon Skeet

@JonSkeet 실제로 적절한 위치에있는 JAR 파일로 컴파일되고 있으며, 그 경우는 정확합니다.

1
@greedybuddha 정적 컨텍스트에서 호출 할 수 없지만을 사용하여 호출 할 수 있습니다 Lifepaths.class. 그것은 왜 그것이 작동 getClassLoader()하도록 허용합니까? (또한, 답변을 게시하십시오!)

보여줄 수 있습니까 Lifepaths.getClass()? Object에 정의 된 정적 메소드는 없습니다 ...
Puce

1
이 답변을 살펴보고를 사용하여 작동하는지 확인하십시오 getResource(String). BTW-나는 그 중 하나를 static맥락 에서 일하게 만드는 데 항상 문제가있었습니다 . 문제는 기본적으로 얻은 클래스 로더가 J2SE 클래스를위한 것입니다. 애플리케이션 자체를위한 컨텍스트 클래스 로더에 액세스해야 합니다.
앤드류 톰슨

답변:


159

Lifepaths.class.getClass().getResourceAsStream(...) 시스템 클래스 로더를 사용하여 리소스를로드하면 JAR이 표시되지 않기 때문에 분명히 실패합니다.

Lifepaths.class.getResourceAsStream(...) Lifepaths 클래스를로드 한 것과 동일한 클래스 로더를 사용하여 자원을로드하며 JAR의 자원에 액세스해야합니다.


129
getResourceAsStream (name)을 호출 할 때 이름은 "/"로 시작해야합니다. 이것이 필요한지 확실하지 않지만 문제가 없습니다.
David

3
나는 오늘 / 어제 오전 8 시부 터 이것을 망쳐 놓았습니다. 저를 구했습니다. 또한 작동하려면 슬래시가 필요했습니다.
kyle

4
또한 원하는 소스는 패키지 계층 구조 외부에있을 수 있습니다. 이 경우 경로에 "../"를 사용하여 한 수준을 올린 다음 다른 경로 분기로 내려가 리소스에 도달해야합니다.
Zon

3
@David
Mz A

5
정보를 추가하려면 /파일이 다른 디렉토리에있는 경우 경로 앞에 를 추가해야합니다 . 예를 들어 initialization/Lifepaths.txt. 파일의 경로가 yout 클래스 와 동일 하지만 (main dir과 같은 리소스 아래) 파일을 아무 이름없이 넣을 수 있습니다 /. 예를 들어 클래스에 다음 경로가있는 경우 src/main/java/paths/Lifepaths.java파일에이 경로가 있어야합니다 src/main/resources/paths/Lifepaths.txt.
Dwhitz

59

규칙은 다음과 같습니다.

  1. JAR 내부에로드 할 파일의 위치를 ​​확인하십시오 (따라서 실제로 JAR에 추가되었는지 확인하십시오)
  2. 절대 경로를 사용하십시오. 경로는 JAR의 루트에서 시작합니다.
  3. 상대 경로를 사용하십시오 : 경로는 getResource / getResoucreAsStream을 호출하는 클래스의 패키지 디렉토리에서 시작합니다

그리고 시도하십시오 :

Lifepaths.class.getResourceAsStream("/initialization/Lifepaths.txt")

대신에

Lifepaths.class.getClass().getResourceAsStream("/initialization/Lifepaths.txt")

(차이가 있는지 확실하지 않지만 전자는 올바른 ClassLoader / JAR을 사용하지만 후자는 확실하지 않습니다)


2
나는 이미이 세 가지를 모두했습니다. 내 질문을 다시 읽어주세요.

귀하의 질문에 따르면 "관련 디렉토리 구조"가 무엇이며 실제로 파일이 JAR에 있는지 여부와 위치를 확인했는지 확실하지 않습니다 (1 단계)
Puce

1
상대 경로에 대한 귀하의 발언은 마침내이 문제를 해결했습니다. 감사!
Paul Bormans

흥미 롭군 "/config.properties"(슬래시 포함)를 수행해야한다는 것이 밝혀졌습니다.
Erk

45

따라서 jar에서 리소스를 가져 오는 여러 가지 방법이 있으며 경로를 다르게 지정 해야하는 구문이 약간 다릅니다.

내가 본 가장 좋은 설명은 JavaWorld 의이 기사입니다 . 여기에 요약하지만 더 알고 싶다면 기사를 확인하십시오.

행동 양식

1) ClassLoader.getResourceAsStream().

형식 : "/"로 구분 된 이름; 선행 "/"없음 (모든 이름은 절대적 임)

예: this.getClass().getClassLoader().getResourceAsStream("some/pkg/resource.properties");

2) Class.getResourceAsStream()

형식 : "/"로 구분 된 이름; 선행 "/"는 절대 이름을 나타냅니다. 다른 모든 이름은 클래스 패키지와 관련이 있습니다.

예: this.getClass().getResourceAsStream("/some/pkg/resource.properties");


두 번째 예는 고장
야누스 Troelsen을

1
두 번째 예는 대답에서 말했듯이 리소스가 어디에 있는지에 따라 다릅니다.
greedybuddha

또한 : 소스 폴더를 새로 고쳐 IDE에서 파일 ( 'some / pkg / resource.properties')을 확인하십시오.
Eric Duminil

15

절대 경로를 사용하지 말고 프로젝트의 'resources'디렉토리를 기준으로하십시오. 'resources'디렉토리에서 MyTest.txt의 내용을 표시하는 빠르고 더러운 코드입니다.

@Test
public void testDefaultResource() {
    // can we see default resources
    BufferedInputStream result = (BufferedInputStream) 
         Config.class.getClassLoader().getResourceAsStream("MyTest.txt");
    byte [] b = new byte[256];
    int val = 0;
    String txt = null;
    do {
        try {
            val = result.read(b);
            if (val > 0) {
                txt += new String(b, 0, val);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    } while (val > -1);
    System.out.println(txt);
}


8

사용중인 ClassLoader에 문제가있는 것 같습니다. contextClassLoader를 사용하여 클래스를로드하십시오. 정적 / 비 정적 방법인지 여부에 관계없이

Thread.currentThread().getContextClassLoader().getResourceAsStream......


5

나는 비슷한 문제에서 나 자신을 발견했다. maven을 사용하고 있기 때문에 다음과 같은 내용을 포함하도록 pom.xml을 업데이트해야했습니다.

   ...
</dependencies>
<build>
    <resources>
        <resource>
            <directory>/src/main/resources</directory>
        </resource>
        <resource>
            <directory>../src/main/resources</directory>
        </resource>
    </resources>
    <pluginManagement>
        ...

해당 폴더의 위치를 ​​지정하려면 여기에 리소스 태그를 기록하십시오. 중첩 된 프로젝트가있는 경우 (내가하는 것처럼) 작업중인 모듈이 아닌 다른 영역에서 리소스를 가져오고 싶을 수 있습니다. 유사한 구성 데이터를 사용하는 경우 각 리포지토리에서 동일한 파일을 유지하는 데 도움이됩니다.


3

나를 위해 일한 것은 파일을 아래에 추가 My Project/Java Resources/src한 다음 사용 하는 것이 었습니다.

this.getClass().getClassLoader().getResourceAsStream("myfile.txt");

내가 경로에이 파일을 추가 명시 적으로 필요하지 않았다 (이에 추가 /src분명히 그 않습니다)


잘못된 자바 문법
Ilya Kharlamov

2

도움이되는지 모르겠지만 제 경우에는 / src / 폴더에 리소스가 있고이 오류가 발생했습니다. 그런 다음 사진을 bin 폴더로 이동하여 문제를 해결했습니다.


2

리소스 디렉토리 (예 : "src")가 클래스 경로에 있는지 확인하십시오 (이클립스의 빌드 경로에서 소스 디렉토리인지 확인하십시오).

기본 클래스 로더에서 clazz를로드하십시오.

그런 다음 src / initialization / Lifepaths.txt를로드하려면

clazz.getResourceAsStream("/initialization/Lifepaths.txt");

이유 : clazz 가있는 디렉토리관련하여 clazz의 클래스 경로clazz.getResourcesAsStream(foo) 에서 foo를 찾습니다 . 선행 "/"는 clazz의 클래스 경로에있는 디렉토리의 루트에서로드합니다.

Tomcat과 같은 컨테이너에 있거나 ClassLoaders로 직접 작업하지 않는 한 이클립스 / 명령 줄 classpath를 유일한 클래스 로더 클래스 경로로 처리 할 수 ​​있습니다.


2

기본 JVM 클래스 로더는 상위 클래스 로더를 사용하여 자원을 먼저로드합니다 deletegate-parent-classloader.

Lifepaths.class.getClass()의 클래스 로더 IS bootstrap classloader, 그래서 getResourceAsStream$ JAVA_HOME에게 단지에 관계없이 사용자의 제공 검색합니다 classpath. 분명히, Lifepaths.txt는 없습니다.

Lifepaths.class의 classloader는 system classpath classloader이므로 getResourceAsStream사용자 정의를 검색 classpath하고 Lifepaths.txt가 있습니다.

를 사용할 때 '/'로java.lang.Class#getResourceAsStream(String name) 시작하지 않는 이름은 접두사 로 추가됩니다 . 이것을 피하려면을 사용하십시오 . 예를 들면 다음과 같습니다.package namejava.lang.ClassLoader#getResourceAsStream

ClassLoader loader = Thread.currentThread().getContextClassLoader();
String resourceName = "Lifepaths.txt";
InputStream resourceStream = loader.getResourceAsStream(resourceName); 

2

대략적으로 말하면 :

getClass().getResource("/") ~ = Thread.currentThread().getContextClassLoader().getResource(".")

프로젝트 구조가 다음과 같다고 가정하십시오.

├── src
   ├── main
   └── test
   └── test
       ├── java
          └── com
              └── github
                  └── xyz
                      └── proj
                          ├── MainTest.java
                          └── TestBase.java
       └── resources
           └── abcd.txt
└── target
    └── test-classes
        ├── com
        └── abcd.txt
// in MainClass.java
this.getClass.getResource("/") -> "~/proj_dir/target/test-classes/"
this.getClass.getResource(".") -> "~/proj_dir/target/test-classes/com/github/xyz/proj/"
Thread.currentThread().getContextClassLoader().getResources(".") -> "~/proj_dir/target/test-classes/"
Thread.currentThread().getContextClassLoader().getResources("/") ->  null

2

Maven을 사용하는 경우 포장이 'jar'이 아닌 'jar'인지 확인하십시오.

<packaging>jar</packaging>

0

실제로 필요한 것은 파일에 대한 완전한 절대 클래스 경로입니다. 따라서 추측하는 대신 ROOT를 찾은 다음 <.war> 파일 구조 중 하나를 더 나은 위치로 파일을 이동하십시오.

URL test1 = getClass().getResource("/");
URL test2 = getClass().getClassLoader().getResource("/");            
URL test3 = getClass().getClassLoader().getResource("../");

logger.info(test1.getPath()); 
logger.info(test2.getPath());
logger.info(test3.getPath());

0

나를 위해 일한 것은 파일을 아래에 배치 한 것입니다.

src/main/java/myfile.log

InputStream is = getClass().getClassLoader().getResourceAsStream("myfile.log");
        
        if (is == null) {
            throw new FileNotFoundException("Log file not provided");
        }

소스 폴더는이며 src/main/JAVA, 비 코드 파일은 여기에 없어야합니다.
leyren

-12

@Emracool ... 대안을 제안하겠습니다. * .txt 파일을로드하려고하는 것 같습니다. FileInputStream()오히려 성가신 getClass().getClassLoader().getResourceAsStream()또는 사용하는 것이 getClass().getResourceAsStream()좋습니다. 최소한 코드가 제대로 실행됩니다.


뭐 ??? 이러한 작업 답변에 -1입니다. 무슨 일이 있어도. 그러나 위의 제안 된 솔루션은 확실히 작동합니다.
Jain
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.