Classpath निर्देशिका से संसाधनों की एक सूची प्राप्त करें


247

मैं किसी दिए गए classpath निर्देशिका से सभी संसाधन नामों की एक सूची प्राप्त करने के लिए एक तरीका ढूंढ रहा हूं, एक विधि जैसा कुछ List<String> getResourceNames (String directoryName)

उदाहरण के लिए, एक classpath निर्देशिका दिया x/y/zफ़ाइलों से युक्त a.html, b.html, c.htmlऔर उप-निर्देशिका d, getResourceNames("x/y/z")एक लौटना चाहिए List<String>निम्नलिखित तार युक्त ['a.html', 'b.html', 'c.html', 'd']

यह फाइलसिस्टम और जार में संसाधनों के लिए काम करना चाहिए।

मुझे पता है कि मैं Fileएस, JarFileएस और URLएस के साथ एक त्वरित स्निपेट लिख सकता हूं , लेकिन मैं पहिया को सुदृढ़ नहीं करना चाहता हूं। मेरा सवाल है, मौजूदा सार्वजनिक रूप से उपलब्ध पुस्तकालयों को देखते हुए, इसे लागू करने का सबसे तेज़ तरीका क्या है getResourceNames? स्प्रिंग और अपाचे कॉमन्स स्टैक दोनों संभव हैं।


1
संबंधित जवाब: stackoverflow.com/a/30149061/4102160
Cfx

इस प्रोजेक्ट की जाँच करें, रिसोर्स फ़ोल्डर स्कैनिंग को हल करता है: github.com/fraballi/resources-folder-scanner
फेलिक्स अबल्ली

जवाबों:


165

कस्टम स्कैनर

अपने खुद के स्कैनर को लागू करें। उदाहरण के लिए:

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

स्प्रिंग फ्रेमवर्क

PathMatchingResourcePatternResolverस्प्रिंग फ्रेमवर्क से उपयोग करें ।

रोन्ममो रिफ्लेक्शंस

अन्य तकनीक विशाल CLASSPATH मूल्यों के लिए रनटाइम पर धीमी हो सकती है। एक तेज़ समाधान रोनमो के रिफ्लेक्शंस एपीआई का उपयोग करना है , जो संकलन समय पर खोज को रोक देता है ।


12
यदि प्रतिबिंब का उपयोग करते हैं, तो आपको वास्तव में जरूरत है:new Reflections("my.package", new ResourcesScanner()).getResources(pattern)
zapp

27
जब जार फ़ाइल से निष्पादित होता है तो क्या पहला समाधान काम करता है? मेरे लिए - यह नहीं है। मैं इस तरह से फ़ाइल पढ़ सकता हूं, लेकिन निर्देशिका नहीं पढ़ सकता।
वैलेंटिना चुमक

5
इस उत्तर में वर्णित पहली विधि - एक संसाधन के रूप में पथ को पढ़ना, काम नहीं करता है जब संसाधन निष्पादन योग्य कोड के रूप में एक ही JAR में हों, कम से कम OpenJDK 1.8 के साथ। विफलता बल्कि विषम है - एक NullPointerException को JVM के फ़ाइल प्रोसेसिंग लॉजिक में गहरे से फेंका गया है। ऐसा लगता है जैसे डिजाइनरों ने वास्तव में संसाधनों के इस उपयोग का अनुमान नहीं लगाया था, और केवल एक आंशिक कार्यान्वयन है। यहां तक ​​कि अगर यह कुछ JDK या वातावरण पर काम करता है, तो यह दस्तावेजी व्यवहार प्रतीत नहीं होता है।
केविन बून

7
आप निर्देशिका को इस तरह से पढ़ सकते हैं, क्योंकि कोई निर्देशिका नहीं है लेकिन फाइल धाराएं हैं। यह उत्तर आधा गलत है।
थॉमस डेकाक्स

4
पहली विधि काम करने के लिए प्रकट नहीं होती है - कम से कम जब विकास पर्यावरण से चल रहा है, तो संसाधनों को मरोड़ने से पहले। getResourceAsStream()निर्देशिका के सापेक्ष पथ के साथ कॉल करने से FileInputStream (फ़ाइल) होती है, जो फ़ाइल निर्देशिका होने पर FileNotFoundException को फेंकने के लिए परिभाषित की जाती है।
एंडी थॉमस

52

यहाँ कोड
स्रोत है : forum.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);
        }
    }
}  

यदि आप स्प्रिंग का उपयोग कर रहे हैं, तो PathMatchingResourcePatternResolver पर एक नज़र डालें


3
प्रश्नकर्ता मानक जावा कक्षाओं का उपयोग करके इसे लागू करना जानता है, लेकिन वह जानना चाहता है कि वह मौजूदा (स्प्रिंग, अपाचे कॉमन्स) पुस्तकालयों का उपयोग कैसे कर सकता है।
जीरोने रोसेनबर्ग

@ जोरेन रोसेनबर्ग वहाँ भी एक और तरीका दिया गया है जिसे आखिरकार चुना गया है :)
जिगर जोशी

7
मैं इस समाधान को उन मामलों के लिए उपयोगी मानता हूं जो आप स्प्रिंग का उपयोग नहीं करते हैं - यह केवल इस सुविधा के स्प्रिंग डेस्यूएस पर निर्भरता जोड़ने के लिए हास्यास्पद होगा। इसलिए आपका धन्यवाद! "गंदा" लेकिन उपयोगी :) PS एक विधि में File.pathSeparatorहार्ड-कोडित के बजाय उपयोग करना चाह सकता है । :getResources
तैमूर

5
कोड उदाहरण सिस्टम पर निर्भर है, इसके बजाय इसका उपयोग करें: final String[] classPathElements = classPath.split(System.pathSeparator);
dcompiled

1
कैविएट: java.class.path सिस्टम प्रॉपर्टी में आपके द्वारा चलाए जा रहे वास्तविक वर्गपथ नहीं हो सकता है। आप वैकल्पिक रूप से कक्षा लोडर को देख सकते हैं और उससे पूछताछ कर सकते हैं कि यह किस URL से कक्षाएं लोड कर रहा है। निश्चित रूप से अन्य चेतावनी यह है कि यदि आप एक SecurityManager के तहत ऐसा कर रहे हैं, तो ZipFile कंस्ट्रक्टर फ़ाइल तक आपकी पहुंच को अस्वीकार कर सकता है, इसलिए आपको उसे पकड़ना चाहिए।
ट्रेजकज

24

प्रतिबिंबों का उपयोग करना

क्लासपाथ पर सब कुछ पाएं:

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

एक और उदाहरण - एक्सटेंशन के साथ सभी फाइलें प्राप्त करें । some.package से .csv :

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

क्या Google निर्भरता आयात करने के लिए समान प्राप्त करने का कोई तरीका है? मैं इस कार्य को करने के लिए इस निर्भरता से बचना चाहूंगा।
Enrico Giurin

1
प्रतिबिंब Google लाइब्रेरी नहीं है।
ल्यूक हचिसन

शायद यह अतीत में था। मेरा उत्तर २०१५ से है और मुझे २०१५ से पहले "गूगल रिफ्लेक्शंस" वाक्यांश का उपयोग करते हुए पुस्तकालय का कुछ संदर्भ मिला था। मुझे लगता है कि कोड लगभग थोड़ा आगे बढ़ गया है और यहां code.google.com से स्थानांतरित होकर यहाँ github कर दिया गया है
NS du Toit

@NSduToit जो शायद Google द्वारा लेखक की दावेदारी का नहीं बल्कि Google कोड पर कोड को होस्ट करने की कलाकारी थी (एक असामान्य गलती नहीं)।
मैट बी

17

यदि आप अपाचे कॉमनसियो का उपयोग करते हैं तो आप फाइल सिस्टम के लिए उपयोग कर सकते हैं (वैकल्पिक रूप से एक्सटेंशन फिल्टर के साथ):

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

और संसाधनों / वर्गपथ के लिए:

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

यदि आपको नहीं पता कि "डाइरेक्टॉय /" फाइलसिस्टम में है या संसाधनों में तो आप जोड़ सकते हैं

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

या

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

कॉल से पहले और संयोजन में दोनों का उपयोग करें ...


23
फाइलें! = संसाधन
djechlin

3
निश्चित रूप से, संसाधन हमेशा फाइलें नहीं हो सकते हैं, लेकिन सवाल फाइलों / निर्देशिकाओं से उत्पन्न संसाधनों के बारे में था। इसलिए उदाहरण कोड का उपयोग करें यदि आप अलग-अलग स्थान की जांच करना चाहते हैं जैसे कि यदि आपके पास कुछ डिफ़ॉल्ट कॉन्फ़िगरेशन के लिए अपने संसाधनों में config.xml है और मौजूद होने पर इसके बजाय बाहरी config.xml को लोड करना संभव हो सकता है ...
Rob

2
और प्रतिध्वनि फ़ाइलें क्यों नहीं हैं? रिसोर्स जिप आर्काइव में संग्रहीत फाइलें हैं। जिन्हें मेमोरी में लोड किया जाता है और विशिष्ट तरीके से एक्सेस किया जाता है लेकिन मैं देख नहीं सकता कि वे फाइल क्यों नहीं हैं। संसाधन का कोई भी अपवाद जो 'संग्रह के भीतर एक फ़ाइल नहीं है'?
माइक

10
काम नहीं कर रहा है (NullPointerException) जब लक्ष्य संसाधन एक निर्देशिका है जो एक JAR फ़ाइल के अंदर रहता है (यानी JAR में मुख्य ऐप और लक्ष्य निर्देशिका शामिल है)
कार्लिटोस वे

12

इसलिए PathMatchingResourcePatternResolver के संदर्भ में यह वही है जो कोड में आवश्यक है:

@Autowired
ResourcePatternResolver resourceResolver;

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

यदि आप उपयोग करने की आवश्यकता है तो सभी छोटे संसाधनों को खोजना चाहते हैं, बस एक छोटा सा जोड़classpath*:config/*.xml
Revau.lt

5

Spring frameworkकी PathMatchingResourcePatternResolverइन बातों के लिए सचमुच कमाल है:

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

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

मावेन निर्भरता:

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

5

रोब की प्रतिक्रिया के संयोजन का उपयोग किया।

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
}

सभी संसाधनों को कैसे प्राप्त करें: यानी /' or या तो शुरू करना। ./
`

3

वसंत के साथ यह आसान है। यह एक फ़ाइल, या फ़ोल्डर, या यहां तक ​​कि कई फाइलें हों, संभावनाएं हैं, आप इसे इंजेक्शन के माध्यम से कर सकते हैं।

यह उदाहरण x/y/zफ़ोल्डर में स्थित कई फ़ाइलों के इंजेक्शन को प्रदर्शित करता है ।

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

यह फाइल सिस्टम के साथ-साथ JAR में संसाधनों के लिए काम करता है।


2
मैं फ़ोल्डर नाम / BOOT-INF / classes / static / stories के तहत प्राप्त करना चाहता हूं। मैं आपका कोड @Value ("classpath: static / stories / *") के साथ आज़माता हूं और यह JAR चलाते समय एक खाली सूची देता है। यह क्लासपाथ में संसाधनों के लिए काम करता है, लेकिन तब नहीं जब वे जार में होते हैं।
PS

@ वाल्यू ("क्लासपैथ: x / y / z / *") निजी संसाधन [] संसाधन; मेरे साथ छल किया। उस के लिए खोज घंटे! धन्यवाद!
रुडोल्फ श्मिट

3

यह काम करना चाहिए (यदि वसंत एक विकल्प नहीं है):

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

इसे हाल ही में घटाया गया - अगर आपको इसमें कोई समस्या मिली, तो कृपया साझा करें, धन्यवाद!
fl0w

1
धन्यवाद @ArnoUnkrig - अगर आप की तरह अपने अद्यतन समाधान साझा करें
fl0w

3

मेरा तरीका, कोई स्प्रिंग नहीं, एक यूनिट परीक्षण के दौरान इस्तेमाल किया गया:

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

(URI) Paths.get पर java.nio.file.FileSystemNotFoundException: जब आप एक जार चलाने काम नहीं करता है,
error1009

1

क्लासपाथ में सभी संसाधनों को सूचीबद्ध करने के लिए सबसे मजबूत तंत्र वर्तमान में क्लासग्राफ के साथ इस पैटर्न का उपयोग करना है , क्योंकि यह नए जेपीएमएस मॉड्यूल सिस्टम सहित क्लासपैथ विनिर्देश तंत्र के व्यापक संभव सरणी को संभालता है। (मैं क्लासग्राफ का लेखक हूं।)

List<String> resourceNames;
try (ScanResult scanResult = new ClassGraph().whitelistPaths("x/y/z").scan()) {
    resourceNames = scanResult.getAllResources().getNames();
}

0

मुझे लगता है कि आप इसे प्राप्त करने के लिए [ जिप फाइल सिस्टम प्रोवाइडर ] [१] का लाभ उठा सकते हैं । FileSystems.newFileSystemइसका उपयोग करते समय ऐसा लगता है कि आप उस ज़िप में ऑब्जेक्ट को "नियमित" फ़ाइल के रूप में मान सकते हैं।

ऊपर दिए गए दस्तावेज़ में:

Java.util में जिप फाइल सिस्टम के लिए विन्यास विकल्प निर्दिष्ट करें FileSystems.newFileSystem । ज़िप फ़ाइल सिस्टम के लिए प्रदाता-विशिष्ट कॉन्फ़िगरेशन गुणों के बारे में जानकारी के लिए [ज़िप फ़ाइल सिस्टम गुण] [2] विषय देखें।

एक बार जब आपके पास एक ज़िप फ़ाइल सिस्टम का एक उदाहरण है, तो आप [ java.nio.file.FileSystem] [३] और [ java.nio.file.Path] [४] वर्गों के तरीकों को कॉपी करने, स्थानांतरित करने और फ़ाइलों का नाम बदलने के साथ-साथ फ़ाइल विशेषताओं को संशोधित करने के लिए भी कर सकते हैं।

jdk.zipfs[11 जावा] [5] में मॉड्यूल के लिए प्रलेखन :

जिप फाइल सिस्टम प्रोवाइडर एक जिप या JAR फाइल को एक फाइल सिस्टम के रूप में मानता है और फाइल की सामग्री में हेरफेर करने की क्षमता प्रदान करता है। जिप फाइल सिस्टम प्रोवाइडर द्वारा FileSystems.newFileSystemस्थापित किया जा सकता है [ ] [६]।

यहाँ एक उदाहरण उदाहरण है जो मैंने आपके उदाहरण संसाधनों का उपयोग करके किया था। ध्यान दें कि एक .zipहै .jar, लेकिन आप अपने कोड को क्लासपैथ संसाधनों का उपयोग करने के लिए अनुकूलित कर सकते हैं:

सेट अप

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

जावा

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

}

उत्पादन

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

मेरे जवाब ने न तो मेरे लिए काम किया, जबकि मैंने अपने संसाधनों को संसाधनों के फ़ोल्डर्स में डाल दिया और उपरोक्त उत्तरों का पालन किया। क्या चाल थी:

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

-5

ऊपर दिए गए @rob की जानकारी के आधार पर, मैंने वह कार्यान्वयन बनाया जो मैं सार्वजनिक डोमेन पर जारी कर रहा हूं:

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
}

हालांकि मैं getResourcesAsStreamएक निर्देशिका पर उस तरह काम करने की उम्मीद नहीं करता था, यह वास्तव में करता है और यह अच्छी तरह से काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.