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

如何为fatJar配置资源文件?基于sbt-assembly的Scala项目运行报错

解决fatJar运行时FileSystemNotFoundException问题

我之前用sbt-assembly打包Scala项目时,也碰到过一模一样的错误!问题根源在于你读取SapConfig.json的方式不对——当资源文件被打包进fatJar后,它不再是操作系统文件系统里的独立文件,而是作为zip归档的一个条目存在。如果还用java.io.File或者直接基于磁盘路径的方式去访问,JVM的ZipFileSystemProvider就找不到对应的文件系统,自然会抛出这个异常。

下面给你两种可靠的解决方案:

方案一:用输入流读取资源(推荐)

这是最通用的方式,不管资源在磁盘还是jar包内都能正常工作。在Scala里可以这么写:

import scala.io.Source
import java.io.InputStream

// 用类加载器获取资源输入流,注意路径开头要加/,对应resources根目录
val configStream: InputStream = getClass.getResourceAsStream("/SapConfig.json")

// 读取内容并处理,记得用完关闭流(推荐用try-with-resources风格)
try {
  val configContent = Source.fromInputStream(configStream).mkString
  // 在这里解析你的SapConfig.json,比如用circe、Jackson等库
} finally {
  if (configStream != null) configStream.close()
}

如果是Scala 2.13及以上版本,还可以用标准库的Using类简化资源管理:

import scala.io.Source
import scala.util.Using

Using(getClass.getResourceAsStream("/SapConfig.json")) { stream =>
  val configContent = Source.fromInputStream(stream).mkString
  // 处理配置逻辑
} match {
  case Right(_) => println("配置读取成功")
  case Left(ex) => println(s"读取配置失败:${ex.getMessage}")
}

方案二:手动访问ZipFileSystem(适合需要Path对象的场景)

如果你的业务逻辑必须用到java.nio.file.Path(比如某些配置库要求传入Path),可以手动打开fatJar对应的ZipFileSystem来访问资源:

import java.nio.file.{Files, Paths, FileSystems}
import java.net.URI

// 获取当前fatJar的路径
val jarUri = getClass.getProtectionDomain.getCodeSource.getLocation.toURI
val jarPath = Paths.get(jarUri)

// 打开Zip文件系统,使用use方法自动关闭(Scala 2.13+支持)
FileSystems.newFileSystem(jarPath, Map("create" -> "false")).use { fs =>
  // 定位到jar内的SapConfig.json
  val configPath = fs.getPath("/SapConfig.json")
  // 读取文件内容
  val configContent = Files.readString(configPath)
  // 处理配置
}

低版本Scala需要用try-finally手动关闭文件系统,避免资源泄漏。

额外检查:确保sbt-assembly正确打包资源

默认情况下sbt-assembly会把src/main/resources下的文件打包进fatJar,但如果你的build.sbt里自定义了assemblyMergeStrategy,要确保资源文件不会被错误过滤或覆盖:

assemblyMergeStrategy in assembly := {
  // 保留resources下的所有文件,遇到重复时取第一个
  case PathList("resources", xs @ _*) => MergeStrategy.first
  // 其他合并策略根据你的项目需求调整
  case x =>
    val oldStrategy = (assemblyMergeStrategy in assembly).value
    oldStrategy(x)
}

核心思路就是:不要用基于磁盘文件的方式访问jar包内的资源,改用输入流或者通过ZipFileSystem来操作,就能解决这个FileSystemNotFoundException问题了。

内容的提问来源于stack exchange,提问作者softshipper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:14:58