Search code examples
javaapache-commons-vfs

Commons VFS fails for different file extension


I am new to Commons VFS and am trying to understand how to open a nested archive. I found an example in the source code that deals with this. This test passes.

@Test
void testTGZ() throws Exception {
    FileSystemManager fsManager = VFS.getManager();
        
    String uri = "tgz:file:///c:/data/nested.tgz!/test.tgz";

    FileObject archive = fsManager.resolveFile(uri);

    FileObject nestedFs = fsManager.createFileSystem(archive);
    // List the children of the archive file
    FileObject[] children = nestedFs.getChildren();
    for (FileObject child : children) {
        System.out.println(child.getURI());
    }
}

whereas this one fails with

org.apache.commons.vfs2.FileSystemException: Could not find a file provider that can handle file "tgz:file:///C:/data/nested.tar.gz!/test.tar.gz".
@Test
void testTarGZ() throws Exception {
    FileSystemManager fsManager = VFS.getManager();

    String uri = "tgz:file:///c:/data/nested.tar.gz!/test.tar.gz";

    FileObject archive = fsManager.resolveFile(uri);

    FileObject nestedFs = fsManager.createFileSystem(archive);
    // List the children of the archive file
    FileObject[] children = nestedFs.getChildren();
    for (FileObject child : children) {
        System.out.println(child.getURI());
    }
}

Other than the names the files are the same so why would one work and not the other? What am I doing wrong?


Solution

  • I came across the same issue and after debugging I was able to find the source of the problem.

    If you take a look at createFileSystem(FileObject file) in the DefaultFileSystemManager class, you can find that it uses the method getScheme(FileObject file) from the FileTypeMap class to detect the scheme:

    /**
     * Find the scheme for the provider of a layered file system.
     * <p>
     * This will check the FileContentInfo or file extension.
     * </p>
     *
     * @return Scheme supporting the file type or null (if unknown).
     */
    public String getScheme(final FileObject file) throws FileSystemException {
        // Check the file's mime type for a match
        final FileContent content = file.getContent();
        final String mimeType = content.getContentInfo().getContentType();
        if (mimeType != null) {
            return mimeTypeMap.get(mimeType);
        }
    
        // no specific mime-type - if it is a file also check the extension
        if (!file.isFile()) {
            return null; // VFS-490 folders don't use extensions for mime-type
        }
        final String extension = file.getName().getExtension();
        return extensionMap.get(extension);
    }
    

    When the extension is tgz, mimeType is evaluated to null and the method tries to get the scheme using the extension itself. In this case, tgz is returned from extensionMap and DefaultFileSystemManager uses that value to select a FileProvider (TarFileProvider in this case).

    When the extension is tar.gz, mimeType is parsed as application/octet-stream which doesn't have any mapping in mimeTypeMap and the methods returns null. In this case, DefaultFileSystemManager fails to create a file system because no FileProvider was found.

    Solution:

    You can specify the FileProvider while creating the file system:

    FileSystemManager manager = VFS.getManager();
    
    // tgz file
    String tgz = "file:/C:/data/nested.tgz";
    FileObject tgzFileObject = manager.resolveFile(tgz);
    FileObject tgzFileSystem = manager.createFileSystem("tgz", tgzFileObject);
    
    // tar.gz file
    String targz = "file:/C:/data/nested.tar.gz";
    FileObject targzFileObject = manager.resolveFile(targz);
    FileObject targzFileSystem = manager.createFileSystem("tgz", targzFileObject);