How to list the files inside a JAR file?

前端 未结 16 2224
温柔的废话
温柔的废话 2020-11-22 00:40

I have this code which reads all the files from a directory.

    File textFolder = new File(\"text_directory\");

    File [] texFiles = textFolder.listFiles         


        
相关标签:
16条回答
  • 2020-11-22 01:39

    I would like to expand on acheron55's answer, since it is a very non-safe solution, for several reasons:

    1. It doesn't close the FileSystem object.
    2. It doesn't check if the FileSystem object already exists.
    3. It isn't thread-safe.

    This is somewhat a safer solution:

    private static ConcurrentMap<String, Object> locks = new ConcurrentHashMap<>();
    
    public void walk(String path) throws Exception {
    
        URI uri = getClass().getResource(path).toURI();
        if ("jar".equals(uri.getScheme()) {
            safeWalkJar(path, uri);
        } else {
            Files.walk(Paths.get(path));
        }
    }
    
    private void safeWalkJar(String path, URI uri) throws Exception {
    
        synchronized (getLock(uri)) {    
            // this'll close the FileSystem object at the end
            try (FileSystem fs = getFileSystem(uri)) {
                Files.walk(fs.getPath(path));
            }
        }
    }
    
    private Object getLock(URI uri) {
    
        String fileName = parseFileName(uri);  
        locks.computeIfAbsent(fileName, s -> new Object());
        return locks.get(fileName);
    }
    
    private String parseFileName(URI uri) {
    
        String schemeSpecificPart = uri.getSchemeSpecificPart();
        return schemeSpecificPart.substring(0, schemeSpecificPart.indexOf("!"));
    }
    
    private FileSystem getFileSystem(URI uri) throws IOException {
    
        try {
            return FileSystems.getFileSystem(uri);
        } catch (FileSystemNotFoundException e) {
            return FileSystems.newFileSystem(uri, Collections.<String, String>emptyMap());
        }
    }   
    

    There's no real need to synchronize over the file name; one could simply synchronize on the same object every time (or make the method synchronized), it's purely an optimization.

    I would say that this is still a problematic solution, since there might be other parts in the code that use the FileSystem interface over the same files, and it could interfere with them (even in a single threaded application).
    Also, it doesn't check for nulls (for instance, on getClass().getResource().

    This particular Java NIO interface is kind of horrible, since it introduces a global/singleton non thread-safe resource, and its documentation is extremely vague (a lot of unknowns due to provider specific implementations). Results may vary for other FileSystem providers (not JAR). Maybe there's a good reason for it being that way; I don't know, I haven't researched the implementations.

    0 讨论(0)
  • 2020-11-22 01:39

    Some time ago I made a function that gets classess from inside JAR:

    public static Class[] getClasses(String packageName) 
    throws ClassNotFoundException{
        ArrayList<Class> classes = new ArrayList<Class> ();
    
        packageName = packageName.replaceAll("\\." , "/");
        File f = new File(jarName);
        if(f.exists()){
            try{
                JarInputStream jarFile = new JarInputStream(
                        new FileInputStream (jarName));
                JarEntry jarEntry;
    
                while(true) {
                    jarEntry=jarFile.getNextJarEntry ();
                    if(jarEntry == null){
                        break;
                    }
                    if((jarEntry.getName ().startsWith (packageName)) &&
                            (jarEntry.getName ().endsWith (".class")) ) {
                        classes.add(Class.forName(jarEntry.getName().
                                replaceAll("/", "\\.").
                                substring(0, jarEntry.getName().length() - 6)));
                    }
                }
            }
            catch( Exception e){
                e.printStackTrace ();
            }
            Class[] classesA = new Class[classes.size()];
            classes.toArray(classesA);
            return classesA;
        }else
            return null;
    }
    
    0 讨论(0)
  • 2020-11-22 01:41

    There are two very useful utilities both called JarScan:

    1. www.inetfeedback.com/jarscan

    2. jarscan.dev.java.net

    See also this question: JarScan, scan all JAR files in all subfolders for specific class

    0 讨论(0)
  • 2020-11-22 01:43

    Here is an example of using Reflections library to recursively scan classpath by regex name pattern augmented with a couple of Guava perks to to fetch resources contents:

    Reflections reflections = new Reflections("com.example.package", new ResourcesScanner());
    Set<String> paths = reflections.getResources(Pattern.compile(".*\\.template$"));
    
    Map<String, String> templates = new LinkedHashMap<>();
    for (String path : paths) {
        log.info("Found " + path);
        String templateName = Files.getNameWithoutExtension(path);
        URL resource = getClass().getClassLoader().getResource(path);
        String text = Resources.toString(resource, StandardCharsets.UTF_8);
        templates.put(templateName, text);
    }
    

    This works with both jars and exploded classes.

    0 讨论(0)
提交回复
热议问题