Sınıfyolu dizininden kaynakların bir listesini alın


247

Bir yöntem gibi bir şey, verilen bir sınıf yolu dizinden tüm kaynak adlarının bir listesini almak için bir yol arıyorum List<String> getResourceNames (String directoryName).

Örneğin, bir sınıf yolu dizini verilen x/y/zdosyaları içeren a.html, b.html, c.htmlve bir alt dizin d, getResourceNames("x/y/z")bir dönmelidir List<String>aşağıdaki dizeleri içeren: ['a.html', 'b.html', 'c.html', 'd'].

Hem dosya sistemindeki kaynaklar hem de kavanozlar için çalışmalıdır.

FileS, JarFiles ve URLs ile hızlı bir pasaj yazabileceğimi biliyorum , ama tekerleği yeniden icat etmek istemiyorum. Benim sorum şu anda mevcut olan kütüphaneler göz önüne alındığında, uygulamanın en hızlı yolu getResourceNamesnedir? Spring ve Apache Commons yığınlarının her ikisi de mümkündür.



Bu projeyi kontrol edin, kaynaklar klasör taramasını çözer: github.com/fraballi/resources-folder-scanner
Felix Aballi

Yanıtlar:


165

Özel Tarayıcı

Kendi tarayıcınızı uygulayın. Örneğin:

private List<String> getResourceFiles(String path) throws IOException {
    List<String> filenames = new ArrayList<>();

    try (
            InputStream in = getResourceAsStream(path);
            BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
        String resource;

        while ((resource = br.readLine()) != null) {
            filenames.add(resource);
        }
    }

    return filenames;
}

private InputStream getResourceAsStream(String resource) {
    final InputStream in
            = getContextClassLoader().getResourceAsStream(resource);

    return in == null ? getClass().getResourceAsStream(resource) : in;
}

private ClassLoader getContextClassLoader() {
    return Thread.currentThread().getContextClassLoader();
}

Bahar Çerçevesi

PathMatchingResourcePatternResolverSpring Framework'ten kullanın .

Ronmamo Yansımalar

Diğer teknikler büyük CLASSPATH değerleri için çalışma zamanında yavaş olabilir. Daha hızlı bir çözüm, aramayı derleme zamanında derleyen ronmamo'nun Reflections API'sini kullanmaktır .


12
Yansımalar kullanıyorsanız, aslında ihtiyacınız olan her şey:new Reflections("my.package", new ResourcesScanner()).getResources(pattern)
zapp 16:13

27
İlk çözüm jar dosyasından yürütüldüğünde çalışır mı? Benim için değil. Dosyayı bu şekilde okuyabilirim, ancak dizini okuyamıyorum.
Valentina Chumak

5
Bu yanıtta açıklanan ilk yöntem - yolu kaynak olarak okumak, kaynaklar yürütülebilir kodla aynı JAR'da, en azından OpenJDK 1.8 ile olduğunda, işe yaramıyor gibi görünüyor. Hata oldukça tuhaf - bir NullPointerException JVM'nin dosya işleme mantığının derinliklerinden atılır. Sanki tasarımcılar kaynakların bu kullanımını gerçekten tahmin etmiyorlardı ve sadece kısmi bir uygulama var. Bazı JDK'larda veya ortamlarda çalışsa bile, bu belgelenmiş bir davranış gibi görünmemektedir.
Kevin Boone

7
Böyle bir dizini okuyamazsınız, çünkü dizinden başka dosya akışı yoktur. Bu cevap yarı yanlış.
Thomas Decaux

4
İlk yöntem işe yaramaz gibi görünüyor - en azından geliştirme ortamından çalışırken, kaynakları bir araya getirmeden önce. getResourceAsStream()Bir dizine göreceli yolla yapılan çağrı, dosya bir dizinse FileNotFoundException öğesini atamak için tanımlanan bir FileInputStream'e (Dosya) yol açar.
Andy Thomas

52

Kod aşağıdadır
Kaynak : forums.devx.com/showthread.php?t=153784

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

/**
 * list resources available from the classpath @ *
 */
public class ResourceList{

    /**
     * for all elements of java.class.path get a Collection of resources Pattern
     * pattern = Pattern.compile(".*"); gets all resources
     * 
     * @param pattern
     *            the pattern to match
     * @return the resources in the order they are found
     */
    public static Collection<String> getResources(
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        final String classPath = System.getProperty("java.class.path", ".");
        final String[] classPathElements = classPath.split(System.getProperty("path.separator"));
        for(final String element : classPathElements){
            retval.addAll(getResources(element, pattern));
        }
        return retval;
    }

    private static Collection<String> getResources(
        final String element,
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        final File file = new File(element);
        if(file.isDirectory()){
            retval.addAll(getResourcesFromDirectory(file, pattern));
        } else{
            retval.addAll(getResourcesFromJarFile(file, pattern));
        }
        return retval;
    }

    private static Collection<String> getResourcesFromJarFile(
        final File file,
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        ZipFile zf;
        try{
            zf = new ZipFile(file);
        } catch(final ZipException e){
            throw new Error(e);
        } catch(final IOException e){
            throw new Error(e);
        }
        final Enumeration e = zf.entries();
        while(e.hasMoreElements()){
            final ZipEntry ze = (ZipEntry) e.nextElement();
            final String fileName = ze.getName();
            final boolean accept = pattern.matcher(fileName).matches();
            if(accept){
                retval.add(fileName);
            }
        }
        try{
            zf.close();
        } catch(final IOException e1){
            throw new Error(e1);
        }
        return retval;
    }

    private static Collection<String> getResourcesFromDirectory(
        final File directory,
        final Pattern pattern){
        final ArrayList<String> retval = new ArrayList<String>();
        final File[] fileList = directory.listFiles();
        for(final File file : fileList){
            if(file.isDirectory()){
                retval.addAll(getResourcesFromDirectory(file, pattern));
            } else{
                try{
                    final String fileName = file.getCanonicalPath();
                    final boolean accept = pattern.matcher(fileName).matches();
                    if(accept){
                        retval.add(fileName);
                    }
                } catch(final IOException e){
                    throw new Error(e);
                }
            }
        }
        return retval;
    }

    /**
     * list the resources that match args[0]
     * 
     * @param args
     *            args[0] is the pattern to match, or list all resources if
     *            there are no args
     */
    public static void main(final String[] args){
        Pattern pattern;
        if(args.length < 1){
            pattern = Pattern.compile(".*");
        } else{
            pattern = Pattern.compile(args[0]);
        }
        final Collection<String> list = ResourceList.getResources(pattern);
        for(final String name : list){
            System.out.println(name);
        }
    }
}  

Spring kullanıyorsanız PathMatchingResourcePatternResolver'a bir göz atın


3
Questioner, standart java sınıflarını kullanarak nasıl uygulanacağını biliyor, ancak mevcut (Spring, Apache Commons) kitaplıklarını nasıl kullanabileceğini bilmek istiyor.
Jeroen Rosenberg

@Jeroen Rosenberg Sonunda seçilen başka bir yol daha var :)
Jigar Joshi

7
Bu çözümü Spring kullanmadığınız durumlar için yararlı buluyorum - yalnızca bu özelliğe bağlı olarak Spring'e bağımlılık eklemek saçma olurdu. Teşekkürler! "Kirli" ama yararlı :) PS One kullanmak isteyebilirsiniz File.pathSeparatoryerine sabit kodlanmış bir :yer getResourcesyöntemle.
Timur

5
Kod örneği sisteme bağlıdır, bunun yerine şunu kullanın: final String[] classPathElements = classPath.split(System.pathSeparator);
dcompiled

1
Dikkat: java.class.path sistem özelliği, altında çalışmakta olduğunuz gerçek sınıf yolunu içermeyebilir. Alternatif olarak sınıf yükleyiciye bakabilir ve sınıfları hangi URL'lerden yüklediğini sorgulayabilirsiniz. Diğer bir uyarı, eğer bunu bir SecurityManager altında yapıyorsanız, ZipFile yapıcısı dosyaya erişiminizi reddedebilir, bu yüzden bunu yakalamanız gerekir.
Trejkaz

24

Yansımaları Kullanma

Her şeyi sınıf yoluna taşıyın:

Reflections reflections = new Reflections(null, new ResourcesScanner());
Set<String> resourceList = reflections.getResources(x -> true);

Başka bir örnek - uzantılı tüm dosyaları elde .csv gelen some.package :

Reflections reflections = new Reflections("some.package", new ResourcesScanner());
Set<String> fileNames = reflections.getResources(Pattern.compile(".*\\.csv"));

Google bağımlılığını içe aktarmak zorunda kalmadan bunu başarmanın bir yolu var mı? Sadece bu görevi yerine getirmek için bu bağımlılığa sahip olmaktan kaçınmak istiyorum.
Enrico Giurin

1
Yansımalar bir Google kütüphanesi değildir.
Luke Hutchison

Belki de geçmişteydi. Benim cevabım 2015 ve ben cümleyi ben kod biraz etrafına taşındı ve code.google.com taşındı bkz 2015 öncesi "Google Yansımalar" seçeneğini kullanarak kütüphaneye bazı başvuru buldunuz burada github için buraya
NS du Toit

@NSduToit, muhtemelen kodu Google Kod'da barındırmanın bir eseriydi (nadir bir hata değil), Google'ın bir yazarlık iddiası değil.
matt b

17

Apache commonsIO kullanıyorsanız dosya sistemi için kullanabilirsiniz (isteğe bağlı olarak uzantı filtresi ile):

Collection<File> files = FileUtils.listFiles(new File("directory/"), null, false);

ve kaynaklar / sınıf yolu için:

List<String> files = IOUtils.readLines(MyClass.class.getClassLoader().getResourceAsStream("directory/"), Charsets.UTF_8);

"Directoy /" dosyasının dosya sisteminde mi yoksa kaynaklarda mı olduğunu bilmiyorsanız,

if (new File("directory/").isDirectory())

veya

if (MyClass.class.getClassLoader().getResource("directory/") != null)

aramalardan önce ve her ikisini birlikte kullanın ...


23
Dosyalar! = Kaynaklar
djechlin

3
Elbette, kaynaklar her zaman dosya olmayabilir, ancak soru dosyalardan / dizinlerden kaynaklanan kaynaklarla ilgilidir. Bu nedenle, örneğin, bazı varsayılan yapılandırmalar için kaynaklarınızda bir config.xml dosyanız varsa ve varsa harici bir config.xml dosyasını yüklemek mümkünse farklı konumu kontrol etmek istiyorsanız örnek kodu kullanın ...
Rob

2
Ve kaynaklar neden dosya değil? Kaynaklar zip arşivinde arşivlenen dosyalardır. Bunlar belleğe yüklenir ve belirli bir şekilde erişilir, ancak neden dosya olmadıklarını göremiyorum. 'Arşivdeki bir dosya' olmayan herhangi bir kaynak örneği?
Mike

10
Hedef kaynak bir JAR Dosyası içinde bulunan bir dizin olduğunda (NullPointerException) çalışmıyor (yani JAR, Ana uygulamayı ve hedef dizini içerir)
Carlitos Way

12

PathMatchingResourcePatternResolver açısından kodda ihtiyaç duyulan şey budur:

@Autowired
ResourcePatternResolver resourceResolver;

public void getResources() {
  resourceResolver.getResources("classpath:config/*.xml");
}

classpath*:config/*.xml
Kullanmanız

5

Spring framework'Ler PathMatchingResourcePatternResolverbunlar için gerçekten harika:

private Resource[] getXMLResources() throws IOException
{
    ClassLoader classLoader = MethodHandles.lookup().getClass().getClassLoader();
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);

    return resolver.getResources("classpath:x/y/z/*.xml");
}

Maven bağımlılığı:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>LATEST</version>
</dependency>

5

Rob'un yanıtı bir arada kullanıldı.

final String resourceDir = "resourceDirectory/";
List<String> files = IOUtils.readLines(Thread.currentThread().getClass().getClassLoader().getResourceAsStream(resourceDir), Charsets.UTF_8);

for(String f : files){
  String data= IOUtils.toString(Thread.currentThread().getClass().getClassLoader().getResourceAsStream(resourceDir + f));
  ....process data
}

tüm kaynaklar nasıl elde edilir : yani ya /' or .` ile başlayarak ya da ./hiçbiri gerçekten işe
yaramaz

3

Bahar ile kolay. İster bir dosya, ister klasör, ister birden fazla dosya olsun, şansınız olabilir, enjeksiyon yoluyla yapabilirsiniz.

Bu örnek, x/y/zklasörde bulunan birden çok dosyanın enjeksiyonunu gösterir .

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

@Service
public class StackoverflowService {
    @Value("classpath:x/y/z/*")
    private Resource[] resources;

    public List<String> getResourceNames() {
        return Arrays.stream(resources)
                .map(Resource::getFilename)
                .collect(Collectors.toList());
    }
}

Dosya sistemindeki ve JAR'lardaki kaynaklar için çalışır.


2
Klasör adlarını / BOOT-INF / classes / static / stories altında almak istiyorum. Kodunuzu @Value ("classpath: static / stories / *") ile deniyorum ve JAR'ı çalıştırırken boş bir liste döndürüyor. Sınıf yolundaki kaynaklar için çalışır, ancak JAR'da olduklarında değil.
PS

@Değer ("sınıfyolu: x / y / z / *") özel Kaynak [] kaynakları; bana hile yaptı. Bunun için arama saatleriniz var! Teşekkürler!
Rudolf Schmidt

3

Bu işe yaramalıdır (bahar bir seçenek değilse):

public static List<String> getFilenamesForDirnameFromCP(String directoryName) throws URISyntaxException, UnsupportedEncodingException, IOException {
    List<String> filenames = new ArrayList<>();

    URL url = Thread.currentThread().getContextClassLoader().getResource(directoryName);
    if (url != null) {
        if (url.getProtocol().equals("file")) {
            File file = Paths.get(url.toURI()).toFile();
            if (file != null) {
                File[] files = file.listFiles();
                if (files != null) {
                    for (File filename : files) {
                        filenames.add(filename.toString());
                    }
                }
            }
        } else if (url.getProtocol().equals("jar")) {
            String dirname = directoryName + "/";
            String path = url.getPath();
            String jarPath = path.substring(5, path.indexOf("!"));
            try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8.name()))) {
                Enumeration<JarEntry> entries = jar.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    String name = entry.getName();
                    if (name.startsWith(dirname) && !dirname.equals(name)) {
                        URL resource = Thread.currentThread().getContextClassLoader().getResource(name);
                        filenames.add(resource.toString());
                    }
                }
            }
        }
    }
    return filenames;
}

Bu son zamanlarda bir aşağı oy aldı - eğer bir sorun bulduysanız, lütfen paylaşın, teşekkür ederim!
fl0w

1
@ArnoUnkrig - isterseniz lütfen güncellenmiş çözümünüzü paylaşın
fl0w

3

Birimin testi sırasında benim yolum yok, bahar yok:

URI uri = TestClass.class.getResource("/resources").toURI();
Path myPath = Paths.get(uri);
Stream<Path> walk = Files.walk(myPath, 1);
for (Iterator<Path> it = walk.iterator(); it.hasNext(); ) {
    Path filename = it.next();   
    System.out.println(filename);
}

bir kavanoz çalıştırdığınızda çalışmıyor: Paths.get (uri) 'de java.nio.file.FileSystemNotFoundException
error1009


0

Bunu başarmak için [ Zip Dosya Sistemi Sağlayıcısı ] [1] 'den yararlanabileceğinizi düşünüyorum . Kullanırken FileSystems.newFileSystembu ZIP içindeki nesneleri "normal" bir dosya gibi ele alabilirsiniz.

Yukarıdaki bağlantılı belgelerde:

FileSystems.newFileSystemYönteme iletilen java.util.Map nesnesindeki zip dosya sistemi için yapılandırma seçeneklerini belirtin . Zip dosyası sisteminin sağlayıcıya özgü yapılandırma özellikleri hakkında bilgi için [Zip Dosya Sistemi Özellikleri] [2] konusuna bakın.

Bir zip dosyası sistemi örneğine sahip olduğunuzda, dosya özniteliklerini değiştirmenin yanı sıra dosyaları kopyalama, taşıma ve yeniden adlandırma gibi işlemleri gerçekleştirmek için [ java.nio.file.FileSystem] [3] ve [ java.nio.file.Path] [4] sınıflarının yöntemlerini çağırabilirsiniz .

jdk.zipfs[Java 11 durumları] [5] içindeki modül belgeleri :

Zip dosyası sistemi sağlayıcısı bir zip veya JAR dosyasını bir dosya sistemi olarak ele alır ve dosyanın içeriğini değiştirme yeteneği sağlar. Zip dosyası sistem sağlayıcısı, FileSystems.newFileSystemyüklenmişse [ ] [6] tarafından oluşturulabilir .

Örnek kaynaklarınızı kullanarak yaptığım bir örnek. A'nın a .zipolduğunu unutmayın .jar, ancak kodunuzu bunun yerine sınıf yolu kaynaklarını kullanacak şekilde uyarlayabilirsiniz:

Kurmak

cd /tmp
mkdir -p x/y/z
touch x/y/z/{a,b,c}.html
echo 'hello world' > x/y/z/d
zip -r example.zip x

Java

import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.Collections;
import java.util.stream.Collectors;

public class MkobitZipRead {

  public static void main(String[] args) throws IOException {
    final URI uri = URI.create("jar:file:/tmp/example.zip");
    try (
        final FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.emptyMap());
    ) {
      Files.walk(zipfs.getPath("/")).forEach(path -> System.out.println("Files in zip:" + path));
      System.out.println("-----");
      final String manifest = Files.readAllLines(
          zipfs.getPath("x", "y", "z").resolve("d")
      ).stream().collect(Collectors.joining(System.lineSeparator()));
      System.out.println(manifest);
    }
  }

}

Çıktı

Files in zip:/
Files in zip:/x/
Files in zip:/x/y/
Files in zip:/x/y/z/
Files in zip:/x/y/z/c.html
Files in zip:/x/y/z/b.html
Files in zip:/x/y/z/a.html
Files in zip:/x/y/z/d
-----
hello world

0

Kaynaklarımı kaynak klasörlerine koyup yukarıdaki cevapları izlememe rağmen cevapların hiçbiri benim için çalışmadı. Hile yapan şey şuydu:

@Value("file:*/**/resources/**/schema/*.json")
private Resource[] resources;

-5

Yukarıdaki @rob bilgilerine dayanarak, kamu malı olarak yayınladığım uygulamayı oluşturdum:

private static List<String> getClasspathEntriesByPath(String path) throws IOException {
    InputStream is = Main.class.getClassLoader().getResourceAsStream(path);

    StringBuilder sb = new StringBuilder();
    while (is.available()>0) {
        byte[] buffer = new byte[1024];
        sb.append(new String(buffer, Charset.defaultCharset()));
    }

    return Arrays
            .asList(sb.toString().split("\n"))          // Convert StringBuilder to individual lines
            .stream()                                   // Stream the list
            .filter(line -> line.trim().length()>0)     // Filter out empty lines
            .collect(Collectors.toList());              // Collect remaining lines into a List again
}

getResourcesAsStreamBir dizinde böyle çalışmayı beklemiyor olsam da , gerçekten işe yarıyor ve iyi çalışıyor.

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.