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

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



답변

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

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


답변

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

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

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


답변

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

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);
    }
});

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


답변

내장 된 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을 닫습니다.


답변

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

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


답변

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

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


답변

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