How to list the files inside a JAR file?

前端 未结 16 2229
温柔的废话
温柔的废话 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 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.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.

提交回复
热议问题