You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3.2升级时依赖出现FileSystemNotFoundException求助

Spring Boot 3.2.0升级后picovoice-java的requiresUnpack配置失效导致FileSystemNotFoundException

问题重现

升级Spring Boot从3.1.6到3.2.0后,启动时抛出以下异常:

java.lang.ExceptionInInitializerError: null
at ai.picovoice.porcupine.Porcupine$Builder.build(Porcupine.java:242)
...
Caused by: java.nio.file.FileSystemNotFoundException: null
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:156)
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:142)
at java.base/java.nio.file.Path.of(Path.java:209)
at java.base/java.nio.file.Paths.get(Paths.java:98)
at ai.picovoice.porcupine.Utils.getResourceDirectory(Utils.java:58)
at ai.picovoice.porcupine.Utils.<clinit>(Utils.java:39)
... 28 common frames omitted

Picovoice依赖中获取资源的核心逻辑为:

private static Path getResourceDirectory() throws RuntimeException {
    URL resourceURL = Porcupine.class.getProtectionDomain().getCodeSource().getLocation();

    Path resourcePath;
    try {
      resourcePath = Paths.get(resourceURL.toURI());
    } catch (URISyntaxException var4) {
      resourcePath = Paths.get(resourceURL.getPath());
    }

    if (resourcePath.toString().endsWith(".jar")) {
      try {
        resourcePath = extractResources(resourcePath);
      } catch (IOException var3) {
        throw new RuntimeException("Failed to extract resources from Porcupine jar.");
      }
    }

    return resourcePath.resolve("porcupine");
}

原Spring Boot 3.1.6中生效的requiresUnpack配置:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>net.indexoutofmj.neptune.NeptuneBot</mainClass>
        <requiresUnpack>
            <dependency>
                <groupId>ai.picovoice</groupId>
                <artifactId>picovoice-java</artifactId>
            </dependency>
        </requiresUnpack>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

原因分析

Spring Boot 3.2.0默认启用了分层打包机制,同时调整了requiresUnpack的处理逻辑:指定依赖的资源会被直接解压到主应用jar的BOOT-INF/classes目录下,而非保持独立jar的形式。

而Picovoice的资源获取逻辑依赖CodeSource.getLocation()获取自身jar路径,此时该路径指向的是主应用jar而非原picovoice-java jar。代码尝试从主jar中提取porcupine资源时,因主jar中并不包含该目录的完整结构(已被解压到类路径),触发FileSystemNotFoundException。

解决方案

方案1:禁用分层打包(快速恢复)

在spring-boot-maven-plugin配置中禁用分层打包,恢复到3.1.6的打包行为:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>net.indexoutofmj.neptune.NeptuneBot</mainClass>
        <requiresUnpack>
            <dependency>
                <groupId>ai.picovoice</groupId>
                <artifactId>picovoice-java</artifactId>
            </dependency>
        </requiresUnpack>
        <!-- 禁用分层打包 -->
        <layers>
            <enabled>false</enabled>
        </layers>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

方案2:修改Picovoice资源获取逻辑(推荐适配)

调整Utils.getResourceDirectory()方法,优先从类加载器读取已解压的porcupine资源,避免依赖CodeSource路径:

private static Path getResourceDirectory() throws RuntimeException {
    // 优先从类路径获取porcupine目录
    URL porcupineResource = Porcupine.class.getClassLoader().getResource("porcupine");
    if (porcupineResource != null) {
        try {
            return Paths.get(porcupineResource.toURI());
        } catch (URISyntaxException e) {
            logger.warning("Failed to resolve porcupine resource from classpath, falling back to jar extraction: " + e.getMessage());
        }
    }

    // 原有jar提取逻辑
    URL resourceURL = Porcupine.class.getProtectionDomain().getCodeSource().getLocation();
    Path resourcePath;
    try {
        resourcePath = Paths.get(resourceURL.toURI());
    } catch (URISyntaxException var4) {
        resourcePath = Paths.get(resourceURL.getPath());
    }

    if (resourcePath.toString().endsWith(".jar")) {
        try {
            resourcePath = extractResources(resourcePath);
        } catch (IOException var3) {
            throw new RuntimeException("Failed to extract resources from Porcupine jar.", var3);
        }
    }

    return resourcePath.resolve("porcupine");
}

方案3:手动提取资源(临时 workaround)

在应用启动时手动将picovoice-java jar中的porcupine目录复制到临时目录,并指定给Picovoice使用:

// 在应用启动类main方法中添加
String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "porcupine";
File porcupineDir = new File(tempDir);
if (!porcupineDir.exists()) {
    try (JarFile jarFile = new JarFile(new File(Paths.get(Porcupine.class.getProtectionDomain().getCodeSource().getLocation()).toURI()))) {
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            if (entry.getName().startsWith("porcupine/")) {
                File destFile = new File(tempDir + File.separator + entry.getName());
                if (entry.isDirectory()) {
                    destFile.mkdirs();
                } else {
                    destFile.getParentFile().mkdirs();
                    try (InputStream is = jarFile.getInputStream(entry);
                         OutputStream os = new FileOutputStream(destFile)) {
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = is.read(buffer)) != -1) {
                            os.write(buffer, 0, len);
                        }
                    }
                }
            }
        }
    } catch (Exception e) {
        throw new RuntimeException("Failed to extract porcupine resources manually", e);
    }
}
// 设置系统属性让Picovoice使用该目录
System.setProperty("ai.picovoice.porcupine.resource.dir", tempDir);

内容的提问来源于stack exchange,提问作者Jes Müller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:18:11