클래스 패스 리소스 (예 : 내가 얻는 것 Class.getResource(String)
) 를 얻는 API가 java.nio.file.Path
있습니까? 이상적으로 Path
는 클래스 경로 리소스와 함께 새롭고 멋진 API 를 사용하고 싶습니다 .
클래스 패스 리소스 (예 : 내가 얻는 것 Class.getResource(String)
) 를 얻는 API가 java.nio.file.Path
있습니까? 이상적으로 Path
는 클래스 경로 리소스와 함께 새롭고 멋진 API 를 사용하고 싶습니다 .
답변:
이것은 나를 위해 작동합니다 :
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
당신이하고 싶은 일을 추측하는 것은 클래스 경로에서 오는 리소스에서 아마도 파일 내에서 Files.lines (...)를 호출하는 것입니다.
jar 파일에 상주하는 경우 getResource가 사용 가능한 경로를 리턴하지 않도록함으로써 경로가 경로 일 때의 개념을 복잡하게 만들었으므로 다음과 같이해야합니다.
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
class.getResource
에는 슬래시가 필요하지만 슬래시 getSystemResourceAsStream
가 접두어로 표시되면 파일을 찾을 수 없습니다.
가장 일반적인 해결책은 다음과 같습니다.
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);
}
}
}
가장 큰 장애물은 우리가 사용해야하지만 기존의 파일 시스템 ( file
URI 또는 Java 9의 모듈 스토리지 와 같이)을 닫지 않아야 하거나 파일 시스템을 직접 열고 닫아야하는 두 가지 가능성을 처리하는 것입니다 (예 : zip / jar 파일).
따라서 위의 솔루션은의 실제 작업을 캡슐화하고 interface
두 경우를 모두 처리하고 두 번째 경우 안전하게 닫히고 Java 7에서 Java 10으로 작동합니다. 새로운 파일 시스템을 열기 전에 이미 열린 파일 시스템이 있는지 여부를 조사하므로 응용 프로그램의 다른 구성 요소가 동일한 zip / jar 파일에 대한 파일 시스템을 이미 연 경우에도 작동합니다.
위에서 언급 한 모든 Java 버전에서 사용할 수 있습니다 (예 : 패키지 내용 ( java.lang
예 Path
:)을 다음과 같이 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을 닫습니다.
newFileSystem
자원은 여러 자원이 계속 열려있게 할 수 있습니다. @raisercostin 부록은 이미 생성 된 파일 시스템을 만들 때 오류를 피하지만 반환 값을 사용하려고 Path
하면을 얻게 ClosedFileSystemException
됩니다. @Holger 응답이 저에게 효과적입니다.
FileSystem
입니다. 당신이 항아리에서 리소스를로드하고 다음 만들 경우 필요한 FileSystem
-은 FileSystem
또한이 같은 항아리에서 다른 리소스를로드 할 수 있습니다. 또한 새로운 것을 만든 후에는 FileSystem
다시 사용하여 리소스를로드하려고 Paths.get(Path)
하면 구현에서 자동으로 new를 사용합니다 FileSystem
.
#getPath(String)
, 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();
}
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)) {
....
}
}
Paths.get(URI)
하고을, and last
반환하는‘ URL.toURI () getResource ()``가 있습니다URL
. 그것들을 서로 연결할 수 있습니다. 그래도 시도하지 않았습니다.