클래스 패스 자원의 java.nio.file.Path


143

클래스 패스 리소스 (예 : 내가 얻는 것 Class.getResource(String)) 를 얻는 API가 java.nio.file.Path있습니까? 이상적으로 Path는 클래스 경로 리소스와 함께 새롭고 멋진 API 를 사용하고 싶습니다 .


3
글쎄, 긴 경로 (pun 의도)를 취하면 을 반환 Paths.get(URI)하고을 , and last 반환하는‘ URL.toURI () getResource ()``가 있습니다 URL. 그것들을 서로 연결할 수 있습니다. 그래도 시도하지 않았습니다.
NilsH

답변:


174

이것은 나를 위해 작동합니다 :

return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());

7
.jar 파일의 자원이 다음을 시도 할 수있는 경우 @VGR`Resource resource = new ClassPathResource ( "usage.txt"); BufferedReader의 리더는 새로운 BufferedReader로를 (새로운 InputStreamReader (resource.getInputStream ())) =;`참조하십시오 stackoverflow.com/questions/25869428/...
zhuguowei

8
@zhuguowei는 스프링 고유의 접근 방식입니다. Spring을 사용하지 않으면 전혀 작동하지 않습니다.
Ryan J. McDonough

2
응용 프로그램이 시스템 클래스 로더에 의존하지 않는 경우Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
ThrawnCA

27

당신이하고 싶은 일을 추측하는 것은 클래스 경로에서 오는 리소스에서 아마도 파일 내에서 Files.lines (...)를 호출하는 것입니다.

jar 파일에 상주하는 경우 getResource가 사용 가능한 경로를 리턴하지 않도록함으로써 경로가 경로 일 때의 개념을 복잡하게 만들었으므로 다음과 같이해야합니다.

Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();

1
귀하의 경우 앞의 "/"가 필요한지 여부는 알 수 없지만 제 경우 class.getResource에는 슬래시가 필요하지만 슬래시 getSystemResourceAsStream가 접두어로 표시되면 파일을 찾을 수 없습니다.
Adam

11

가장 일반적인 해결책은 다음과 같습니다.

interface IOConsumer<T> {
    void accept(T t) throws IOException;
}
public static void processRessource(URI uri, IOConsumer<Path> action) throws IOException {
    try {
        Path p=Paths.get(uri);
        action.accept(p);
    }
    catch(FileSystemNotFoundException ex) {
        try(FileSystem fs = FileSystems.newFileSystem(
                uri, Collections.<String,Object>emptyMap())) {
            Path p = fs.provider().getPath(uri);
            action.accept(p);
        }
    }
}

가장 큰 장애물은 우리가 사용해야하지만 기존의 파일 시스템 ( fileURI 또는 ​​Java 9의 모듈 스토리지 와 같이)을 닫지 않아야 하거나 파일 시스템을 직접 열고 닫아야하는 두 가지 가능성을 처리하는 것입니다 (예 : zip / jar 파일).

따라서 위의 솔루션은의 실제 작업을 캡슐화하고 interface두 경우를 모두 처리하고 두 번째 경우 안전하게 닫히고 Java 7에서 Java 10으로 작동합니다. 새로운 파일 시스템을 열기 전에 이미 열린 파일 시스템이 있는지 여부를 조사하므로 응용 프로그램의 다른 구성 요소가 동일한 zip / jar 파일에 대한 파일 시스템을 이미 연 경우에도 작동합니다.

위에서 언급 한 모든 Java 버전에서 사용할 수 있습니다 (예 : 패키지 내용 ( java.langPath:)을 다음과 같이 s 로 나열 ) .

processRessource(Object.class.getResource("Object.class").toURI(), new IOConsumer<Path>() {
    public void accept(Path path) throws IOException {
        try(DirectoryStream<Path> ds = Files.newDirectoryStream(path.getParent())) {
            for(Path p: ds)
                System.out.println(p);
        }
    }
});

Java 8 이상에서는 람다 식 또는 메서드 참조를 사용하여 실제 작업을 나타낼 수 있습니다.

processRessource(Object.class.getResource("Object.class").toURI(), path -> {
    try(Stream<Path> stream = Files.list(path.getParent())) {
        stream.forEach(System.out::println);
    }
});

똑같이


Java 9 모듈 시스템의 최종 릴리스는 위의 코드 예제를 위반했습니다. JRE는 경로 /java.base/java/lang/Object.class를 일관성없이 반환하지만 경로 는 Object.class.getResource("Object.class")이어야합니다 /modules/java.base/java/lang/Object.class. /modules/부모 경로가 존재하지 않는 것으로보고 될 때 누락 된 부분을 앞에 추가하면이 문제를 해결할 수 있습니다 .

processRessource(Object.class.getResource("Object.class").toURI(), path -> {
    Path p = path.getParent();
    if(!Files.exists(p))
        p = p.resolve("/modules").resolve(p.getRoot().relativize(p));
    try(Stream<Path> stream = Files.list(p)) {
        stream.forEach(System.out::println);
    }
});

그런 다음 모든 버전 및 저장 방법으로 다시 작동합니다.


1
이 솔루션은 훌륭하게 작동합니다! 이것이 디렉토리 클래스 경로와 jar 클래스 경로의 모든 자원 (파일, 디렉토리)에서 작동하는지 확인할 수 있습니다. 이것은 Java 7 이상에서 많은 자원을 복사하는 방법입니다.
Mitchell Skaggs

10

내장 된 Zip 파일 시스템 공급자 의 도움 으로이 작업을 수행 할 수 있습니다 . 그러나 리소스 URI를 직접 전달하면 Paths.get작동하지 않습니다. 대신, 항목 이름없이 jar URI에 대한 zip 파일 시스템을 먼저 작성한 다음 해당 파일 시스템의 항목을 참조해야합니다.

static Path resourceToPath(URL resource)
throws IOException,
       URISyntaxException {

    Objects.requireNonNull(resource, "Resource URL cannot be null");
    URI uri = resource.toURI();

    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        return Paths.get(uri);
    }

    if (!scheme.equals("jar")) {
        throw new IllegalArgumentException("Cannot convert to Path: " + uri);
    }

    String s = uri.toString();
    int separator = s.indexOf("!/");
    String entryName = s.substring(separator + 2);
    URI fileURI = URI.create(s.substring(0, separator));

    FileSystem fs = FileSystems.newFileSystem(fileURI,
        Collections.<String, Object>emptyMap());
    return fs.getPath(entryName);
}

최신 정보:

코드가 새로운 FileSystem 객체를 열지 만 결코 닫지 않기 때문에 위의 코드에 리소스 누수가 포함되어 있다는 것이 올바르게 지적되었습니다. 가장 좋은 방법은 Holger의 답변과 같은 방식으로 소비자와 유사한 작업자 개체를 전달하는 것입니다. 작업자가 Path와 관련하여 필요한 모든 작업을 수행 할 수있을 정도로 ZipFS FileSystem을 연 다음 (근무자가 나중에 사용하기 위해 Path 객체를 저장하려고하지 않는 한) FileSystem을 닫습니다.


11
새로 작성된 fs에주의하십시오. 동일한 jar을 사용하는 두 번째 호출은 이미 존재하는 파일 시스템에 대해 불평하는 예외를 발생시킵니다. try (FileSystem fs = ...) {return fs.getPath (entryName);}를 수행하는 것이 좋습니다. 또는 캐시 된 경우 더 고급 처리를 수행하십시오. 현재 양식에서는 위험합니다.
raisercostin

3
잠재적으로 닫히지 않은 새 파일 시스템의 문제 외에도 스키마 간의 관계와 새 파일 시스템을 열어야 할 필요성 및 URI 내용과의 수수께끼에 대한 가정은 솔루션의 유용성을 제한합니다. 작업을 단순화하고 새로운 Java 9 클래스 스토리지와 같은 새로운 체계를 동시에 처리하는 일반적인 접근 방식을 보여주는 새로운 답변 을 설정했습니다 . 또한 ... 응용 프로그램 내에서 다른 사람이 이미 파일 시스템을 열었습니다 (또는 메소드가 같은 단지 두 번 호출) 할 때 작동합니다
홀거

이 솔루션의 사용에 따라 폐쇄되지 않은 newFileSystem자원은 여러 자원이 계속 열려있게 할 수 있습니다. @raisercostin 부록은 이미 생성 된 파일 시스템을 만들 때 오류를 피하지만 반환 값을 사용하려고 Path하면을 얻게 ClosedFileSystemException됩니다. @Holger 응답이 저에게 효과적입니다.
José Andias

나는 닫지 않을 것 FileSystem입니다. 당신이 항아리에서 리소스를로드하고 다음 만들 경우 필요한 FileSystem-은 FileSystem또한이 같은 항아리에서 다른 리소스를로드 할 수 있습니다. 또한 새로운 것을 만든 후에는 FileSystem다시 사용하여 리소스를로드하려고 Paths.get(Path)하면 구현에서 자동으로 new를 사용합니다 FileSystem.
NS du Toit

#getPath(String), FileSystem객체 에서 메소드 를 사용할 필요가 없습니다 .
NS du Toit

5

Paths수업 자료에서 읽을 수있는 작은 도우미 방법을 작성했습니다 . 리소스 자체의 이름뿐만 아니라 리소스를 저장 한 클래스에 대한 참조 만 있으면되므로 사용하기가 매우 편리합니다.

public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
    URL url = resourceClass.getResource(resourceName);
    return Paths.get(url.toURI());
}  

1

jar 파일 내부의 자원에서 URI를 작성할 수 없습니다. 간단히 임시 파일에 작성한 다음 사용할 수 있습니다 (java8).

Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);

1

java8에서 NIO를 사용하여 resources 폴더에서 파일 읽기

public static String read(String fileName) {

        Path path;
        StringBuilder data = new StringBuilder();
        Stream<String> lines = null;
        try {
            path = Paths.get(Thread.currentThread().getContextClassLoader().getResource(fileName).toURI());
            lines = Files.lines(path);
        } catch (URISyntaxException | IOException e) {
            logger.error("Error in reading propertied file " + e);
            throw new RuntimeException(e);
        }

        lines.forEach(line -> data.append(line));
        lines.close();
        return data.toString();
    }

0

https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html에 언급 된대로 jar 파일에서 자원을 읽으려면 파일 시스템을 정의해야합니다 . 아래 코드를 사용하여 jar 파일에서 리소스를 읽었습니다.

Map<String, Object> env = new HashMap<>();
try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {

        Path path = fs.getPath("/path/myResource");

        try (Stream<String> lines = Files.lines(path)) {
            ....
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.