我可以在没有硬编码的情况下在 Windows 上运行的 Java 中构建 Linux 路径吗?

Posted

技术标签:

【中文标题】我可以在没有硬编码的情况下在 Windows 上运行的 Java 中构建 Linux 路径吗?【英文标题】:Can I build a Linux path in Java running on Windows without hard-coding? 【发布时间】:2018-11-21 16:50:57 【问题描述】:

我正在使用 Docker 容器来托管 Selenium 集线器和一些节点,我需要帮助这些测试找到静态 html 文件。我已将本地驱动器上的一个文件夹映射到 Docker 节点。

我的代码(Java 10,在 Windows 10 上运行)如下所示:

private URL getTestPageUrl() 
    var folder = Common.getString(Prop.testAssetFolder);
    var pathToTestPage = Paths.get(folder, "selectorTestPage.html");
    URL url = null;
    try  url = pathToTestPage.toUri().toURL(); 
    catch (MalformedURLException e)  /* Most pointlessly checked exception ever. */ 
    return url;

pathToTestPage 输出为\testAssets\selectorTestPage.html.toUri().toURL() 然后转换为file:/C:/testAssets/selectorTestPage.html,这当然不能在 Linux 上运行。

显然,我可以将一些文件夹和文件名以及一些硬编码的/ 粘合在一起以获取 Linux 格式的此路径,但有没有更优雅的方法?

【问题讨论】:

【参考方案1】:

你应该可以使用 NIO.2 的FileSystemProvider,特别是sun.nio.fs.UnixFileSystemProvider。

这可以让你做类似的事情

FileSystemProvider fsp = new UnixFileSystemProvider();
Path path = fsp.getPath(pathToTestPage.toUri());

【讨论】:

在适用于 Windows 的 JDK 10 中是否真的可以使用 UnixFileSystemProvider(或就此而言 LinuxFileSystemProvider)? Visual Studio Code 通常非常擅长引入正确的命名空间,但它的行为就像它不存在一样。 @RyanLundy 它可能不存在(包sun.nio.fs 听起来确实像一个内部包,你不能依赖它在那里)。如果您决定使用它,您可能需要单独打包它......只是想给您选择。 sun.nio.fs 包在那里,但它似乎不包括那些 Unix/Linux 类,好像编译器标志将它们排除在外。它确实包含WindowsFileSystemProvider 和一些抽象类,这让我想知道其他类是否被故意排除在编译中。 在 Java 10 上,Java 模块系统可能会默认隐藏该模块?【参考方案2】:

阅读前的注意事项FileSystem 的 Javadoc 和相关的 API 将许多方法的行为描述为依赖于实现。这是有道理的,因为不同的文件系统有不同的规则。话虽如此,默认的FileSystems 定义得相当好(因为它们为主要操作系统建模)。

静态方法Paths.get(String, String...) 委托给平台默认的FileSystem。这与您从FileSystems.getDefault() 获得的FileSystem 相同。这意味着在 Windows 上它委托给 WindowsFileSystem,在 Linux 上它委托给 LinuxFileSystem(如果是这个名字的话),等等。实际创建Path 对象是上述FileSystem 的责任。这涉及添加正确的分隔符和验证每个名称之类的事情。分隔符由FileSystem.getSeparator() 公开定义。

当您在 Windows 上调用 Paths.get("foo", "bar", "file.txt") 时,返回的 Path 将是 foo\bar\file.txt。在 Linux 上,它将变为 foo/bar/file.txt。我什至发现它是相当宽容的,至少在 Windows 上,传递一个实际上是带有错误分隔符的路径的名称。例如,在 Windows 上调用 Paths.get("foo/bar/file.txt") 仍会返回 foo\bar\file.txt

您还可以致电path.toUri().toURL()toUri() 状态的 Javadoc(强调我的):

此方法构造一个 absolute URI,其方案与标识提供者的 URI 方案相同。方案特定部分的确切形式高度依赖于提供者。

在默认提供程序的情况下,URI 是分层的,具有绝对的路径组件。

...

投掷

...

SecurityException - 在默认提供程序的情况下,并且安装了安全管理器,toAbsolutePath 方法会引发安全异常。

在相对 Path 上调用 Path.toUri() 将首先找到绝对 Path。而且,基于Javadoc,我会说默认的FileSystems 将通过调用Path.toAbsolutePath() 获得绝对的Path。这意味着 Path 被解析为基于您当前所在平台的绝对路径。如您所见,Windows 上的调用导致将C:/ 添加为根。这不会发生在例如 Linux 机器上。此外,现在是 URI 定义了路径中使用的分隔符。而toURL()的调用也是由URI定义的。


在使用java.nio 时,显然平台之间仍然存在问题。总会有你必须注意的问题和不兼容性。一个例子是 Windows 有一个大小写不敏感的文件系统,而 Linux 是(我相信)大小写敏感的。

了解您的代码是否正确跨平台的唯一可靠方法是在每个目标平台上运行测试。

【讨论】:

【参考方案3】:

如果 String 结果是可接受的,请考虑结合 Paths.get(...) 和 Apache Commons IO 的强大功能:

Path path = Paths.get(folder, "selectorTestPage.html"); 
return FilenameUtils.separatorsToUnix(path.toString());

【讨论】:

以上是关于我可以在没有硬编码的情况下在 Windows 上运行的 Java 中构建 Linux 路径吗?的主要内容,如果未能解决你的问题,请参考以下文章