Sınıf yolu kaynağı için java.nio.file.Path


143

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 PathAPI'ları sınıf yolu kaynaklarıyla kullanmak istiyorum .


3
Peki, uzun yolu seçerek (pun amaçlı) Paths.get(URI), sonra returnsURL.toURI () , and last getResource () `a döner URL. Bunları birbirine zincirleyebilirsiniz. Yine de denemedim.
NilsH

Yanıtlar:


175

Bu benim için çalışıyor:

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

7
@VGR .jar dosyasındaki kaynaklar bu `Kaynak kaynağı = yeni ClassPathResource (" usage.txt ") 'yi deneyebilirse; BufferedReader okuyucu = yeni BufferedReader (yeni InputStreamReader (resource.getInputStream ())); `` bakınız stackoverflow.com/questions/25869428/…
zhuguowei

8
@zhuguowei, bu Bahar'a özgü bir yaklaşım. Spring kullanılmadığında hiç işe yaramaz.
Ryan J. McDonough

2
Uygulamanız sistem Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
sınıfı

27

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

1
Senin durumunda önceki "/" gerekli olup olmadığını bilmiyorum, ama benim durumumda class.getResourcebir eğik çizgi gerektirir ama getSystemResourceAsStreameğik çizgi ile ön ekli dosyayı bulamıyor.
Adam

11

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 ( fileURI'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 Paths 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.classiç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.


1
Bu çözüm harika çalışıyor! Bunun hem dizin sınıfyolları hem de jar sınıfyollarındaki tüm kaynaklarla (dosyalar, dizinler) çalıştığını doğrulayabilirim. Java 7 + 'da çok sayıda kaynağın kopyalanması kesinlikle bu şekilde yapılmalıdır.
Mitchell Skaggs

10

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.


11
Yeni oluşturulan fs'ye dikkat edin. Aynı kavanozu kullanan ikinci bir çağrı, mevcut bir dosya sistemi hakkında şikayetçi bir istisna atar. Denemek daha iyi olacaktır (FileSystem fs = ...) {return fs.getPath (entryName);} veya bu önbelleğe alınmış olanın daha gelişmiş bir işlem yapmasını istiyorsanız. Mevcut formda risklidir.
raisercostin

3
Potansiyel olarak kapalı olmayan yeni dosya sistemi sorununun yanı sıra, şemalar arasındaki ilişki ve yeni bir dosya sistemi açmanın gerekliliği ile URI içeriğiyle kafa karıştırmanın gerekliliği hakkındaki çözümlerin çözümün faydasını sınırlandırmaktadır. Bir kurdum yeni cevap aynı anda yeni bir Java 9 sınıf depolama gibi operasyon ve kolları yeni planlar basitleştiren bir genel yaklaşım gösterilmektedir. Uygulama içindeki başka biri dosya sistemini zaten açtığında da çalışır (veya yöntem aynı kavanoz için iki kez çağrılır)…
Holger

Bu çözümün kullanımına bağlı olarak, kapalı olmayan newFileSystemsonsuza 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 Pathbir alırsınız ClosedFileSystemException. @Holger yanıtı benim için iyi çalışıyor.
José Andias

Ben kapatmazdım FileSystem. Bir Kavanoz bir kaynağı yüklemek ve daha sonra oluşturursanız gerekli FileSystem- FileSystemAyrıca, aynı Jar diğer kaynaklar yük sağlayacaktır. Ayrıca, yeni oluşturduktan sonra FileSystemkaynağı kullanarak yeniden yüklemeyi deneyebilirsiniz Paths.get(Path)ve uygulama otomatik olarak yeniyi kullanır FileSystem.
NS du Toit

Yani kullanmak gerekmez #getPath(String)üzerinde yöntemini FileSystemnesne.
NS du Toit

5

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

1

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

1

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

0

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)) {
            ....
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.