Sınıf yolu kaynağı (örneğin, ne alacağım Class.getResource(String)
) olarak almak için bir API var mı java.nio.file.Path
? İdeal olarak, şık yeni Path
API'ları sınıf yolu kaynaklarıyla kullanmak istiyorum .
Sınıf yolu kaynağı (örneğin, ne alacağım Class.getResource(String)
) olarak almak için bir API var mı java.nio.file.Path
? İdeal olarak, şık yeni Path
API'ları sınıf yolu kaynaklarıyla kullanmak istiyorum .
Yanıtlar:
Bu benim için çalışıyor:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
Ne yapmak istediğinizi tahmin etmek, sınıf yolundan gelen bir kaynak üzerinde Files.lines (...) 'ı çağırır - muhtemelen bir kavanozun içinden.
Oracle, bir yolun bir Yol olduğu fikrini, bir jar dosyasında bulunuyorsa getResource'un kullanılabilir bir yol döndürmesini sağlamadan kıvrıldığından, yapmanız gereken şudur:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
class.getResource
bir eğik çizgi gerektirir ama getSystemResourceAsStream
eğik çizgi ile ön ekli dosyayı bulamıyor.
En genel çözüm şöyledir:
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);
}
}
}
Ana engel, kullanmamız gereken mevcut bir dosya sistemine sahip olmak, ancak yakın olmamak ( file
URI'ler veya Java 9'un modül depolama alanı gibi) veya dosya sistemini kendimiz açmak ve güvenli bir şekilde kapatmak ( zip / jar dosyaları).
Bu nedenle, yukarıdaki çözüm gerçek eylemi bir interface
alır, her iki durumu da ele alır, daha sonra ikinci durumda güvenli bir şekilde kapanır ve Java 7'den Java 10'a çalışır. Yeni bir tane açmadan önce zaten açık bir dosya sistemi olup olmadığını araştırır. uygulamanızın başka bir bileşeninin aynı zip / jar dosyası için zaten bir dosya sistemi açmış olması durumunda da çalışır.
Yukarıda belirtilen tüm Java sürümlerinde kullanılabilir, örneğin bir paketin içeriğini ( java.lang
örnekte) aşağıdaki gibi Path
s olarak listelemek için :
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 veya daha yenisiyle, gerçek eylemi göstermek için lambda ifadelerini veya yöntem referanslarını kullanabilirsiniz;
processRessource(Object.class.getResource("Object.class").toURI(), path -> {
try(Stream<Path> stream = Files.list(path.getParent())) {
stream.forEach(System.out::println);
}
});
aynısını yapmak.
Java 9'un modül sisteminin son sürümü yukarıdaki kod örneğini kırmıştır. JRE tutarsız yolunu döndürür /java.base/java/lang/Object.class
için Object.class.getResource("Object.class")
olması gerektiği oysa /modules/java.base/java/lang/Object.class
. Bu /modules/
, üst yolun var olmadığı bildirildiğinde eksik öğenin eklenmesiyle giderilebilir :
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);
}
});
Ardından, yine tüm sürümler ve depolama yöntemleri ile çalışacaktır.
Yerleşik Zip Dosya Sistemi sağlayıcısının yardımıyla bunu yapabileceğiniz ortaya çıkıyor . Ancak, doğrudan bir kaynak URI'sini aktarmak Paths.get
çalışmaz; bunun yerine, önce giriş adı olmadan jar URI'si için bir zip dosya sistemi oluşturmalı, ardından bu dosya sistemindeki girdiye başvurmalısınız:
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);
}
Güncelleme:
Yukarıdaki kodun kaynak sızıntısı içerdiği doğru bir şekilde belirtilmiştir, çünkü kod yeni bir FileSystem nesnesi açar ancak asla kapatmaz. En iyi yaklaşım, Holger'ın cevabının yaptığı gibi, Tüketici benzeri bir işçi nesnesini geçmek. ZipFS Dosya Sistemini, çalışanın Yol ile ne gerekiyorsa yapması için yeterince uzun açın (çalışan Yol nesnesini daha sonra kullanmak üzere saklamaya çalışmadığı sürece), ardından Dosya Sistemini kapatın.
newFileSystem
sonsuza kadar açıkta asılı birden fazla kaynağa yol açabilir. @Raisercostin addendum zaten oluşturulmuş bir dosya sistemi oluşturmaya çalışırken hatayı önler, ancak iade kullanmaya çalıştığınızda Path
bir alırsınız ClosedFileSystemException
. @Holger yanıtı benim için iyi çalışıyor.
FileSystem
. Bir Kavanoz bir kaynağı yüklemek ve daha sonra oluşturursanız gerekli FileSystem
- FileSystem
Ayrıca, aynı Jar diğer kaynaklar yük sağlayacaktır. Ayrıca, yeni oluşturduktan sonra FileSystem
kaynağı kullanarak yeniden yüklemeyi deneyebilirsiniz Paths.get(Path)
ve uygulama otomatik olarak yeniyi kullanır FileSystem
.
#getPath(String)
üzerinde yöntemini FileSystem
nesne.
Paths
Sınıf kaynaklarınızdan okumak için küçük bir yardımcı yöntem yazdım . Sadece kaynaklarınızı sakladığınız sınıfın referansına ve kaynağın kendisinin adına ihtiyaç duyduğundan kullanımı oldukça kullanışlıdır.
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
URL url = resourceClass.getResource(resourceName);
return Paths.get(url.toURI());
}
Jar dosyasının içindeki kaynaklardan URI oluşturamazsınız. Basitçe geçici dosyaya yazıp kullanabilirsiniz (java8):
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
Java8'de NIO'yu kullanarak kaynaklar klasöründen bir Dosya okuyun
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();
}
Kaynak dosyasını jar dosyasından okumak için https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html adresinde belirtildiği şekilde Dosya Sistemi'ni tanımlamanız gerekir . Aşağıdaki kodları ile jar dosyasından kaynak okumak için başarı:
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)
, sonra returnsURL.toURI (), and last
getResource () `a dönerURL
. Bunları birbirine zincirleyebilirsiniz. Yine de denemedim.