URI 的编码路径在内置于 JAR 中或未内置于 JAR 中时表现不同

Encoding Path to URI behaves differently when built into JAR or not

我调用了 HashMap 的 containsKey 方法,我希望 return 为真。如果我将我的程序编译为 .class 文件并且 运行 那样,它 returns 是真的,但是如果我构建一个 JAR 那么相同的调用 returns false 原因看不懂。

我已经调试了 .class 和 JAR 版本(使用 http://www.eclipsezone.com/eclipse/forums/t53459.html 中描述的远程连接的 JAR),在这两种情况下,HashMap 似乎都包含密钥 I'我正在检查。

HashMap 使用 URI 对象作为键。以下是每个调试会话中显示的变量内容:

当 运行 作为 .class 文件时

HashMap 键:java.net.URI = file:/E:/SSD%20App%20Libraries/Google%20Drive/Programming/Bet%20Matching/Java%20Sim/target/classes/simfiles/paytables/

URIToCheck:java.net.URI = file:///E:/SSD%20App%20Libraries/Google%20Drive/Programming/Bet%20Matching/Java%20Sim/target/classes/simfiles/paytables/

结果:GameTreeItemsMap.containsKey(URIToCheck)true

当 运行 作为 JAR

HashMap 键:java.net.URI = jar:file:/E:/SSD%20App%20Libraries/Google%20Drive/Programming/Bet%20Matching/Java%20Sim/out/artifacts/JavaSim_jar/Java%20Sim.jar!/simfiles/paytables/

URIToCheck:java.net.URI = jar:file:///E:/SSD%2520App%2520Libraries/Google%2520Drive/Programming/Bet%2520Matching/Java%2520Sim/out/artifacts/JavaSim_jar/Java%2520Sim.jar!/simfiles/paytables/

结果:GameTreeItemsMap.containsKey(URIToCheck)false

在这两种情况下,我希望该方法 return true。 URI 在 JAR 中的行为是否有所不同?怎么回事?

在此先感谢您的帮助!

编辑 1 正如向我指出的那样,JAR 案例中的 URIToCheck 被双重编码(%2520 而不是 %20)。下面是生成 URIToCheck 的代码。我使用 walkFileTree 方法。

        Files.walkFileTree(paytableHomePath, new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {

                Path parentPath = dir.getParent();

                URI parentURIToCheck = parentPath.toUri();

                boolean testContain = GameTreeItemsMap.containsKey(parentURIToCheck);

                return FileVisitResult.CONTINUE;
            }

在 JAR 的情况下,URI parentURIToCheck 是双重编码的(对于 space 应该有 %20 的是 %5250),在 .class 的情况下是这样的不会发生。知道为什么吗?

这与HashMap无关。您似乎在 Java 的 Zip File System Provider 中发现了一个错误,即它将路径转换为双重编码的 URI。

我找不到它的现有错误,所以我提交了一个。 (有this related bug, whose fix I suspect is the cause of this one.) Update: This is Java bug 8131067.

这是我在 Java 1.8.0_45-b14 中编写的用于演示问题的程序。将路径中包含一个或多个空格的 .jar 文件作为第一个命令行参数传递。

import java.util.Map;
import java.util.Collections;
import java.net.URI;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;

public class JarPathTest {
    public static void main(String[] args)
    throws IOException {

        Path zip = Paths.get(args[0]);

        URI zipURI = URI.create("jar:" + zip.toUri());
        System.out.println(zipURI);

        Map<String, String> env = Collections.emptyMap();

        try (FileSystem fs = FileSystems.newFileSystem(zipURI, env)) {
            Path root = fs.getPath("/");
            System.out.println(root.toUri());
        }
    }
}

您可以通过将解码后的 URI 视为仍然是百分比编码来解决此问题:

parentURIToCheck = URI.create(
    parentURIToCheck.getScheme() + ":" +
    parentURIToCheck.getSchemeSpecificPart());