[java] jar 내에서 리소스 파일 읽기

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

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를 사용하고 있으며 파일이있는 폴더가 클래스 폴더에 있습니다.



답변

리소스를 파일 로 지정하려고하지 않고 클래스 로더 에게 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파일) 내의 항목을 읽을 수 없습니다 .

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


답변

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

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

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

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


답변

나는이 문제가 전에 있었고 로딩을위한 대체 방법을 만들었다. 기본적으로 첫 번째 방법은 .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;
    }
}


답변

지금까지 (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);
}


답변

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

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


답변

파일로 읽으려면 여전히 비슷한 해결책이 있다고 생각합니다.

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


답변

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