Docker容器内访问classpath资源抛出FileSystemNotFoundException问题
问题根源
核心原因是不同环境下classpath资源的存储形式不同,Paths.get()无法直接解析Jar包内部的资源URI:
- 本地运行时,项目未打包成Jar,
schemas目录是操作系统文件系统上的真实目录,返回的资源URI是标准file:协议,Paths.get()可以直接识别并解析为磁盘路径,所以运行正常。 - Docker环境运行时,依赖的
mylibrary库已经是Jar包形式,schemas目录存放在Jar包内部,返回的资源URI为jar:file:协议,路径里的!是Jar资源分隔符,代表!后的路径是Jar包内部的虚拟路径,JVM默认没有为jar协议注册对应文件系统实现,直接调用Paths.get()就会抛出FileSystemNotFoundException或FileNotFoundException。
你之前使用FileSystem.getDefault()只能获取操作系统默认的本地文件系统,只能处理Jar包外的真实磁盘路径,自然无法覆盖Jar内资源的场景,所以只能临时解决部分问题。
可行解决方法
由于你需要将合法路径传给第三方schema校验库使用,可根据校验库的支持情况选择以下方案:
方案1:资源拷贝到临时目录(兼容性最高)
如果校验库只支持读取本地磁盘路径,可将Jar包内的schemas目录整体拷贝到系统临时目录,再将临时目录路径传给校验库:
// 创建临时目录 Path tempSchemaDir = Files.createTempDirectory("runtime_schemas"); // 遍历schemas下所有资源,逐个写入临时目录(这里以单个文件为例,目录需要递归遍历) String schemaFileName = "xxx.schema"; try (InputStream in = getClass().getClassLoader().getResourceAsStream("schemas/" + schemaFileName)) { if (in == null) { throw new RuntimeException("找不到schema资源: " + schemaFileName); } Files.copy(in, tempSchemaDir.resolve(schemaFileName)); } // 将tempSchemaDir的绝对路径传给校验库即可
方案2:初始化Jar文件系统(性能更高)
如果校验库支持Java NIO的自定义FileSystem路径,可以先为Jar包创建专属文件系统,再解析内部路径:
URL resource = getClass().getClassLoader().getResource("schemas"); URI uri = resource.toURI(); Path schemaPath = null; FileSystem jarFs = null; if ("jar".equals(uri.getScheme())) { // 提取Jar包本身的路径 String jarUriStr = uri.getSchemeSpecificPart().split("!")[0]; // 为Jar包创建文件系统 jarFs = FileSystems.newFileSystem(URI.create(jarUriStr), Collections.emptyMap()); // 解析Jar内部的schemas路径 schemaPath = jarFs.getPath("/schemas"); } else { // 非Jar环境直接解析 schemaPath = Paths.get(uri); } // 使用schemaPath传入校验库,使用完毕后关闭jarFs释放资源
内容的提问来源于stack exchange,提问作者Said Savci
相关产品推荐
相关产品推荐

