jar 내에서 리소스 파일 읽기


242

항아리 내에서 다음과 같이 리소스를 읽고 싶습니다.

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

//Read the file

Eclipse에서 실행할 때 정상적으로 작동하지만 항아리로 내 보내면 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를 사용하고 있으며 파일이있는 폴더가 클래스 폴더에 있습니다.


jar 파일의 디렉토리에서 파일 번호를 지정하여 파일을 읽으려면 Stackoverflow-Link
Michael Hegner의

1
원래 질문에 Spring이 포함되어 있는지 확실하지 않습니다. 이전 주석의 링크는 다른 질문의 Spring 특정 답변을 나타냅니다. 나는 getResourceAsStream여전히 문제에 대한 더 간단하고 이식 가능한 솔루션 이라고 생각 합니다.
Drew MacInnis

답변:


398

리소스를 파일 로 지정하려고하지 않고 클래스 로더 에게 getResourceAsStream 을 통해 대신 리소스에 대한 InputStream 을 반환 하도록 요청하십시오 .

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

만큼 file.txt자원이 클래스 경로에 볼 수 있습니다 다음이 접근 방식에 관계없이 여부와 같은 방식으로 작동합니다 file.txt리소스가있는 classes/디렉토리 또는 내부 jar.

URI is not hierarchicaljar 파일 내의 리소스에 대한 URI는 다음과 같이 뭔가에 가고 있기 때문에 발생합니다 file:/example.jar!/file.txt. 평범한 오래된 File 과 같이 jar( zip파일) 내의 항목을 읽을 수 없습니다 .

이것은 다음에 대한 답변으로 잘 설명됩니다.


3
고맙습니다. 이것은 매우 도움이되었고 코드가 완벽하게 작동하지만 한 가지 문제 InputStream가 있습니다. File.exists()게임이 기본 파일을 사용할지 여부를 알 수 있도록 (예 :)이 있는지 확인해야합니다. 감사.
PrinceCJC

1
아 그리고 BTW가 getClass().getResource("**/folder**/file.txt")작동하게 된 이유 는 내 jar와 같은 디렉토리에 폴더가 있기 때문입니다. :).
PrinceCJC

5
getResourceAsStream리소스가 존재하지 않는 경우 null을 반환하여 "존재"테스트가 될 수 있습니다.
Drew MacInnis

1
BTW, 당신은 오타가 있습니다 : 그것은 BufferredReader가 아니라 BufferedReader 여야합니다 (나중에 여분의 'r'에 주목하십시오)
mailmindlin

2
그리고 물론 ... inputStream과 BufferedReader를 닫는 것을 잊지 마십시오
Noremac

26

jar 파일에 액세스하려면 두 가지 옵션이 있습니다.

  • 패키지 이름과 일치하는 디렉토리 구조에 파일을 배치하고 (.jar 파일을 추출한 후 .class 파일과 동일한 디렉토리에 있어야 함) 다음을 사용하여 액세스하십시오. getClass().getResourceAsStream("file.txt")

  • 파일을 루트에 놓고 (.jar 파일을 추출한 후 루트에 있어야 함) 다음을 사용하여 액세스하십시오. Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

jar을 플러그인으로 사용하는 경우 첫 번째 옵션이 작동하지 않을 수 있습니다.


이 훌륭한 답변에 대해 대단히 감사합니다 ... 두 번째 옵션의 또 다른 이점은 getClass ()가 정적 메소드가 아닌 인스턴스에서만 작동하기 때문에 기본 메소드에서도 작동한다는 것입니다.
Dan Ortega

6

나는이 문제가 전에 있었고 로딩을위한 대체 방법을 만들었다. 기본적으로 첫 번째 방법은 .jar 파일 내에서 작동하고 두 번째 방법은 일식 또는 다른 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

문제는 특정 타사 라이브러리에 입력 스트림이 아닌 파일 경로 이름이 필요하다는 것입니다. 대부분의 답변은이 문제를 해결하지 못합니다.

이 경우 한 가지 해결 방법은 리소스 내용을 임시 파일로 복사하는 것입니다. 다음 예제는 jUnit 's를 사용합니다 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

48
프로그램이 항아리에 빌드되면 내부에서 작동하지 않습니다
Akshay Kasar

1
문자열로 변환하고 로컬에 먼저 저장하지 않으면 jar에서 작동하지 않습니다.
smoosh911

0

올바른 구분 기호로 작업해야합니다. /상대 경로의 모든 것을로 대체 했습니다 File.separator. 이것은 IDE에서 잘 작동했지만 빌드 JAR에서는 작동하지 않았습니다.


0

나는 이것이 자바에서도 작동해야한다고 생각한다. 내가 사용하는 다음 코드는 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"로 입력하십시오. 파일을 찾을 수 없음 예외가 발생하지 않으면 파일에 .txt 확장자를 추가하지 마십시오.


-1

클래스 경로에서 ROOT 경로로 읽을 클래스 로더를 사용할 수 있습니다 (처음에 "/"없이)

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

-1

스프링을 사용하는 경우 다음 방법을 사용하여 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에 오신 것을 환영합니다. 이것은 질문에 대한 답변을 제공하지 않습니다. 평판 이 충분 하면 모든 게시물 에 댓글 수 있습니다 . 또한 내가 대신 할 수있는 일을 확인하십시오 .
thewaywe는

파일에서 줄 바꿈이 제거됩니다!
elad.chen

-1

어떤 이유로 든 classLoader.getResource()웹 응용 프로그램을 WildFly 14에 배포 할 때 항상 null을 반환했습니다. classLoader를 가져 getClass().getClassLoader()오거나 Thread.currentThread().getContextClassLoader()null을 반환합니다.

getClass().getClassLoader() API 의사는 말합니다.

"클래스에 대한 클래스 로더를 리턴합니다. 일부 구현에서는 부트 스트랩 클래스 로더를 표시하기 위해 널을 사용할 수 있습니다.이 메소드는 부트 스트랩 클래스 로더에 의해이 클래스가로드 된 경우 이러한 구현에서 널을 리턴합니다."

WildFly를 사용 중이고 웹 응용 프로그램에서 시도한 경우

request.getServletContext().getResource()리소스 URL을 반환했습니다. 여기서 request는 ServletRequest의 객체입니다.


-2

아래 코드는 스프링 부트 (kotlin)에서 작동합니다.

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.