如何在Scala中从资源文件夹读取文件?

129

我有一个像下面这样的文件夹结构:

- main
-- java
-- resources 
-- scalaresources
--- commandFiles 

那个文件夹里有我需要阅读的文件。

以下是代码:

def readData(runtype: String, snmphost: String, comstring: String, specificType:  String): Unit = {
  val realOrInvFile = "/commandFiles/snmpcmds." +runtype.trim // these files are under commandFiles folder, which I have to read. 
    try {
      if (specificType.equalsIgnoreCase("Cisco")) {
        val specificDeviceFile: String = "/commandFiles/snmpcmds."+runtype.trim+ ".cisco"
        val realOrInvCmdsList = scala.io.Source.fromFile(realOrInvFile).getLines().toList.filterNot(line => line.startsWith("#")).map{
          //some code 
        }
        val specificCmdsList = scala.io.Source.fromFile(specificDeviceFile).getLines().toList.filterNot(line => line.startsWith("#")).map{
          //some code
        }
      }
    } catch {
      case e: Exception => e.printStackTrace
    }
  }
}

为什么Andreas Neumann提供的答案没有被接受,如果您有任何后续问题,请在评论中提出。-1。 - Vishrant
7个回答

231

Scala中的资源与Java中的完全相同。 最好遵循Java最佳实践,将所有资源放在src/main/resourcessrc/test/resources中。

示例文件夹结构:

testing_styles/
├── build.sbt
├── src
│   └── main
│       ├── resources
│       │   └── readme.txt

Scala 2.12.x && 2.13.x 读取资源

要读取资源,对象Source提供了方法fromResource

import scala.io.Source
val readmeText : Iterator[String] = Source.fromResource("readme.txt").getLines

阅读资源在2.12之前(由于兼容性原因仍是我最喜欢的版本)

要读取资源,您可以使用 getClass.getResource getClass.getResourceAsStream

val stream: InputStream = getClass.getResourceAsStream("/readme.txt")
val lines: Iterator[String] = scala.io.Source.fromInputStream( stream ).getLines

更友好的错误反馈 (2.12.x && 2.13.x)

为避免难以调试的 Java 空指针异常,请考虑:

import scala.util.Try
import scala.io.Source
import java.io.FileNotFoundException

object Example {

  def readResourceWithNiceError(resourcePath: String): Try[Iterator[String]] = 
    Try(Source.fromResource(resourcePath).getLines)
      .recover(throw new FileNotFoundException(resourcePath))
 }

值得知道

请记住,当资源是 jar 的一部分时,getResourceAsStream 也可以正常工作。但是返回 URL 的 getResource 在创建文件时可能会导致问题。

在生产中

在生产代码中,建议确保源代码再次关闭。


如果使用getResource并将其转换为File,可能会出现什么问题?你能提供一个链接吗? - akauppi
2
在某些情况下可能会出现空指针:https://dev59.com/inNA5IYBdhLWcg3wfN6O - Andreas Neumann
1
这段代码可能会在getResourceAsStream中留下未关闭的处理程序。 - Sisso
4
不要忘记“关闭”源代码。 - Guillaume Massé
1
谢谢!在“更好的错误反馈(2.12.x)”部分,字节类型不匹配。那么内存泄漏呢?资源不应该被关闭吗? - Albert Bikeev
显示剩余3条评论

33

对于 Scala >= 2.12,使用 Source.fromResource

scala.io.Source.fromResource("located_in_resouces.any")

14
重要提示:使用Source.fromResource时,不需要像使用getResourceAsStream一样在开头加上斜杠。 - vossad01
6
请注意,这是2.12及以上版本。 - rbellamy
2.10 版本怎么样? - Jaydev

21

Scala 2.12及以上版本的一行解决方案

val source_html = Source.fromResource("file.html").mkString

重要提示(感谢 @anentropic 提供的评论):在使用 Source.fromResource 方法时,不需要加上初始的斜杠。


1
只是重复下面另一个答案中@vossad01提供的有用评论:“重要提示:使用Source.fromResource时,不要输入您在getResourceAsStream中使用的初始斜杠”。 - Anentropic

9
import scala.io.Source

object Demo {

  def main(args: Array[String]): Unit = {

    val ipfileStream = getClass.getResourceAsStream("/folder/a-words.txt")
    val readlines = Source.fromInputStream(ipfileStream).getLines
    readlines.foreach(readlines => println(readlines))

  }

}

1
当你从网站复制内容时,请附上原作者的链接。尊重知识产权,给予应有的荣誉。参考:http://fruzenshtein.com/scala-working-with-resources-folders-files/ - ForeverLearner
当代码相同时,并不意味着它被复制了。 - Sri

4

在scala的资源文件夹中,可以通过如下方式访问所需文件:

val file = scala.io.Source.fromFile(s"src/main/resources/app.config").getLines().mkString

3
Scala 2.11版本中,如果getLines方法不能完全满足您的需求,您还可以将文件从jar包中复制到本地文件系统。以下是一个示例代码片段,它从/resources目录读取二进制google .p12格式API密钥,将其写入/tmp目录,然后使用该文件路径字符串作为输入传递给spark-google-spreadsheetswrite 方法进行操作。
sbt-native-packagersbt-assembly世界中,将文件复制到本地对于scalatest二进制文件测试也很有用。只需要从资源中弹出它们并复制到本地,运行测试,然后删除即可。
import java.io.{File, FileOutputStream}
import java.nio.file.{Files, Paths}

def resourceToLocal(resourcePath: String) = {
  val outPath = "/tmp/" + resourcePath
  if (!Files.exists(Paths.get(outPath))) {
    val resourceFileStream = getClass.getResourceAsStream(s"/${resourcePath}")
    val fos = new FileOutputStream(outPath)
    fos.write(
      Stream.continually(resourceFileStream.read).takeWhile(-1 !=).map(_.toByte).toArray
    )
    fos.close()
  }
  outPath
}

val filePathFromResourcesDirectory = "google-docs-key.p12"
val serviceAccountId = "[something]@drive-integration-[something].iam.gserviceaccount.com"
val googleSheetId = "1nC8Y3a8cvtXhhrpZCNAsP4MBHRm5Uee4xX-rCW3CW_4"
val tabName = "Favorite Cities"

import spark.implicits
val df = Seq(("Brooklyn", "New York"), 
          ("New York City", "New York"), 
          ("San Francisco", "California")).
          toDF("City", "State")

df.write.
  format("com.github.potix2.spark.google.spreadsheets").
  option("serviceAccountId", serviceAccountId).
  option("credentialPath", resourceToLocal(filePathFromResourcesDirectory)).
  save(s"${googleSheetId}/${tabName}")

0

"resources"文件夹必须位于源根目录下。如果使用IntelliJ,请在左侧的项目文件夹中查找蓝色文件夹。例如:AppName/src/main/scala或Project/scala/../main/等。

如果使用val stream: InputStream = getClass.getResourceAsStream("/readme.txt"),请不要忘记"/"(正斜杠),假设readme.txt是资源文件夹内的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接