异常处理——java.nio.file.FileSystemNotFoundException

2,496 阅读2分钟

写在前面

今天在读取resources下的文件使用时,发现在idea启动运行时可以正常读取,但是部署到服务器上运行时则不可以,报错java.nio.file.FileSystemNotFoundException,这个问题困扰了我挺长时间。所以这里写一个demo来模拟记录一下。

实践

目标是读取resources下的test.xml文件来完成功能。

image-20211031164639292.png

本地idea读取代码如下

public class ResourceReader {

    static {
        try {
            URI uri = ResourceReader.class.getClassLoader().getResource("test.xml").toURI();
            Path source = Paths.get(uri);
            System.out.println(source.getFileName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ResourceReader resourceReader = new ResourceReader();
    }
}

可以正常运行并输出文件名test.xml。

但打包代码部署到服务器上后运行时报错如下:

image-20211031165042943.png

于是很纳闷,在stackflow搜了下这个报错,大致定位了下问题可以知道应该是服务器上使用jar包运行导致与本地环境不一样引起的报错。于是我将项目在本地打成jar包后准备debug做下对比是哪里的问题。jar包debug方式参考。

在windows的idea下执行时,调试进入Paths.get这个方法,可以发现如果uri.getScheme()为file时,FileSystems默认创建了一个DefaultFileSystemHolder,返回了一个默认加载的FileSystemProvider。

image-20211031174910152.png

而通过jar包执行时,uri.getScheme() 为jar,会走到try to find provider的代码块,这里加载了两个Provider,一个是WindowsFileSystemProvider,一个是ZipFileSystemProvider,继续debug下去会走到ZipFileSystemProvider的getPath方法

image-20211031202232145.png

进去之后发现该Provider的filesystems为空,所以传入var1获取的值为null

image-20211101102513254.png

结合stackflow上的回答可知对于给定的uri,需要自己初始化filesystem,

image-20220221173504513.png

当然,其回答也是参考了zipfilesystemprovider官网给的示例。于是得出如下修改代码,

public class ResourceReader {

    static {
        try {
            URI uri = ResourceReader.class.getClassLoader().getResource("test.xml").toURI();
            Map<String, String> env = new HashMap<>();
            env.put("create", "true");
            FileSystem zipfs = FileSystems.newFileSystem(uri, env);
            Path source = Paths.get(uri);
            System.out.println(source.getFileName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ResourceReader resourceReader = new ResourceReader();
    }
}

重新打包运行一下,就可以正常运行了。

image-20211101115507880.png

但是,需要注意的是改动之后的代码只适用于执行jar包时的环境,在idea下直接执行是会报错的,因为idea下选用的provider是WindowsFileSystemProvider,可以发现该provider实现的newFileSystem 会直接报错,需要注释掉。当然你也可以选择判断var是否带jar来判断是否应该装载FileSystem

image-20211101122536974.png

总结

解决问题时追根溯源,可以提高自己解决问题的能力,也能学到更多的知识,比如上面提到的gradle项目打包,jar包debug等,如果有优秀的源码也能顺便学习一下。