Java 7 zip file system provider doesn't seem to accept spaces in URI

前端 未结 4 713
滥情空心
滥情空心 2020-12-18 18:35

I have been testing all possible variations and permutations, but I can\'t seem to construct a FileSystemProvider with the zip/jar scheme for a path (URI) that contains spac

相关标签:
4条回答
  • 2020-12-18 19:12

    The jar: URIs should have the escaped zip-URI in its scheme-specific part, so your jar: URI is simply wrong - it should rightly be double-escaped, as the jar: scheme is composed of the host URI, !/ and the local path.

    However, this escaping is only implied and not expressed by the minimal URL "specification" in JarURLConnection. I agree however with the raised bug in JRE that it should still accept single-escaped, although that could lead to some strange edge-cases not being supported.

    As pointed out by tornike and evermean in another answer, the easiest is to do FileSystems.newFileSystem(path, null) - but this does not work when you want to pass and env with say "create"=true.

    Instead, create the jar: URI using the component-based constructor:

    URI jar = new URI("jar", path.toUri().toString(), null);
    

    This would properly encode the scheme-specific part.

    As a JUnit test, which also confirms that this is the escaping used when opening from a Path:

    @Test
    public void jarWithSpaces() throws Exception {
        Path path = Files.createTempFile("with several spaces", ".zip");
        Files.delete(path);
    
        // Will fail with FileSystemNotFoundException without env:
        //FileSystems.newFileSystem(path, null);
    
        // Neither does this work, as it does not double-escape:
        // URI jar = URI.create("jar:" + path.toUri().toASCIIString());                
    
        URI jar = new URI("jar", path.toUri().toString(), null);
        assertTrue(jar.toASCIIString().contains("with%2520several%2520spaces"));
    
        Map<String, Object> env = new HashMap<>();
        env.put("create", "true");
    
        try (FileSystem fs = FileSystems.newFileSystem(jar, env)) {
            URI root = fs.getPath("/").toUri();    
            assertTrue(root.toString().contains("with%2520several%2520spaces"));
        } 
        // Reopen from now-existing Path to check that the URI is
        // escaped in the same way
        try (FileSystem fs = FileSystems.newFileSystem(path, null)) {
            URI root = fs.getPath("/").toUri();
            //System.out.println(root.toASCIIString());
            assertTrue(root.toString().contains("with%2520several%2520spaces"));
        }
    }
    

    (I did a similar test with "with\u2301unicode\u263bhere" to check that I did not need to use .toASCIIString())

    0 讨论(0)
  • 2020-12-18 19:28

    This is a bug in Java 7 and it has been marked as fixed in Java 8 (see Bug ID 7156873). The fix should also be backported to Java 7, but at the moment it's not determined that which update will have it (see Bug ID 8001178).

    0 讨论(0)
  • 2020-12-18 19:31

    Actually further analysis does seem to indicate there is a problem with the ZipFileSystemProvider. The uriToPath(URI uri) method contained within the class executes the following snippet:

    String spec = uri.getSchemeSpecificPart();
    int sep = spec.indexOf("!/");
    if (sep != -1)
      spec = spec.substring(0, sep);
    return Paths.get(new URI(spec)).toAbsolutePath();
    

    From the JavaDocs of URI.getSchemeSpecificPart() we can see the following:

    The string returned by this method is equal to that returned by the getRawSchemeSpecificPart method except that all sequences of escaped octets are decoded.

    This same string is then passed back as an argument into the new URI() constructor. Since any escaped octets are de-escaped by getSchemeSpecificPart(), if the original URI contained any escape characters, they will not be propagated to the new URI - hence the exception.

    A potential workaround - loop through all the available filesystem providers and get the reference to the one who's spec equals "jar". Then use that to create a new filesystem based on path only.

    0 讨论(0)
  • 2020-12-18 19:31

    There are two methods to create a filesystem:

    FileSystem fs = FileSystems.newFileSystem(uri, env);

    FileSystem fs = FileSystems.newFileSystem(zipfile, null);

    When there is a space in a filename together with the above solution for creating a uri. It also works if you use a different method that doesn't take a uri as argument.

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