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

如何获取经sbt打包项目中指定资源目录内的文件列表?

解决sbt universal打包后资源目录访问问题

嘿,我完全懂你遇到的坑——当用sbt universal:packageBin打包后,src/main/resources下的资源不再是磁盘上的真实目录,而是被打包进了归档文件(比如jar或者zip包)里,这时候直接用File类去操作肯定会翻车,因为getResource返回的URL根本不是普通的文件路径。

为什么原来的方法行不通?

  • getClass.getResourceAsStream("/public/images/"):这个方法只对单个文件有效,目录本身不是一个可读取的“文件”,所以返回null是正常的。
  • getClass.getResource("/public/images/"):打包后这个URL的协议是jar:而不是file:,File类根本无法识别这种归档内的路径,所以isDirectory()返回false,调用listFiles()自然会抛出异常。

最优解决方案分两种情况

情况1:你知道目录下的具体文件名(比如asd1.png、asd2.png)

这是最简单的场景,直接单个加载资源就好,不管是开发环境还是打包后都能正常工作:

// 加载单个图片资源
val imageStream = getClass.getResourceAsStream("/public/images/asd1.png")
// 之后就可以用这个流做你需要的操作,比如读取图片内容

情况2:需要动态遍历目录下的所有文件

如果必须要遍历目录,就得兼容**开发时(磁盘目录)和打包后(归档内)**两种场景。可以写一个工具方法来处理不同的URL协议:

import java.io._
import java.net.{URL, URLDecoder}
import java.util.jar.JarFile

object ResourceUtils {
  // 传入资源目录的路径(必须以/开头,比如"/public/images/")
  def listResourceFiles(dirPath: String): List[String] = {
    val resourceUrl = getClass.getResource(dirPath)
    if (resourceUrl == null) return Nil

    resourceUrl.getProtocol match {
      // 开发环境:资源在磁盘目录
      case "file" =>
        val dir = new File(URLDecoder.decode(resourceUrl.getPath, "UTF-8"))
        if (dir.isDirectory) {
          dir.listFiles()
            .filter(_.isFile)
            .map(file => s"$dirPath${file.getName}")
            .toList
        } else Nil

      // 打包后:资源在Jar/Zip归档内
      case "jar" =>
        // 解析Jar文件路径
        val jarFullPath = resourceUrl.getPath.substring(5, resourceUrl.getPath.indexOf("!"))
        val jarFile = new JarFile(URLDecoder.decode(jarFullPath, "UTF-8"))
        val entries = jarFile.entries()
        val resourcePaths = collection.mutable.ListBuffer[String]()

        while (entries.hasMoreElements) {
          val entry = entries.nextElement()
          val entryName = entry.getName
          // 筛选出目标目录下的文件(排除子目录)
          if (entryName.startsWith(dirPath.stripSuffix("/")) && !entry.isDirectory) {
            resourcePaths.addOne(s"/$entryName")
          }
        }
        jarFile.close()
        resourcePaths.toList

      // 其他协议(比如war等),根据需要扩展
      case _ => Nil
    }
  }
}

使用示例:

// 获取public/images下所有文件的路径
val imagePaths = ResourceUtils.listResourceFiles("/public/images/")
// 遍历加载每个资源
imagePaths.foreach { path =>
  val stream = getClass.getResourceAsStream(path)
  // 处理资源流...
}

额外注意事项

  • 确保传入的目录路径以/开头,避免匹配到类似public/imagesOther这种无关路径
  • 记得对URL路径解码,防止路径里的空格、特殊字符导致错误
  • 使用JarFile后一定要关闭,避免资源泄漏
  • 如果你的项目用了Play、Spring Boot这类框架,直接用它们提供的资源加载工具会更省心(比如Spring的ResourceLoader)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:19:37