129

I have a folder structure like below:

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

and in that folders I have my files that I have to read. Here is the code:

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
    }
  }
}
Matteo Guarnerio
  • 720
  • 2
  • 9
  • 26
Pradip Karad
  • 1,377
  • 2
  • 9
  • 8
  • why answer provided by Andreas Neumann was not accepted if you have any followup question please comment on it. -1. – Vishrant Jul 05 '18 at 16:59

7 Answers7

231

Resources in Scala work exactly as they do in Java. It is best to follow the Java best practices and put all resources in src/main/resources and src/test/resources.

Example folder structure:

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

Scala 2.12.x && 2.13.x reading a resource

To read resources the object Source provides the method fromResource.

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

reading resources prior 2.12 (still my favourite due to jar compatibility)

To read resources you can use getClass.getResource and getClass.getResourceAsStream .

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

nicer error feedback (2.12.x && 2.13.x)

To avoid undebuggable Java NPEs, consider:

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))
 }

good to know

Keep in mind that getResourceAsStream also works fine when the resources are part of a jar, getResource, which returns a URL which is often used to create a file can lead to problems there.

in Production

In production code I suggest to make sure that the source is closed again.

Andreas Neumann
  • 10,734
  • 1
  • 32
  • 52
  • What kind of problems could occur if using getResource and turning it into a File? Can you provide a link? – akauppi Jan 24 '16 at 21:30
  • 2
    In some circumstanceas a null Pointer : http://stackoverflow.com/questions/941754/how-to-get-a-path-to-a-resource-in-a-java-jar-file – Andreas Neumann Jan 25 '16 at 08:36
  • 1
    This code is probably leaving open handler for getResourceAsStream. – Sisso Jan 04 '17 at 08:32
  • 4
    dont forget to `close` the Source – Guillaume Massé Feb 15 '17 at 09:09
  • 1
    Thanks! Byt types don't match in _Nicer error feedback (2.12.x)_ section. And what about memory leaks? Shouldn't resource be closed? – Albert Bikeev May 03 '19 at 12:58
  • fixed the type annotations. put more focus on closing the resource by adding a heading. – Andreas Neumann May 15 '19 at 17:52
  • It's bad practice not to _always_ close resources. Non-production code can eventually slip into production for various reasons and not closing resources also erodes developers understanding of the underlying API. – marko Dec 31 '20 at 08:18
  • Hey mister Neumann, this works fine for files. Any idea for folders? https://stackoverflow.com/questions/66722366/how-to-list-all-files-from-resources-folder-with-scala – Jan Mar 20 '21 at 14:24
33

For Scala >= 2.12, use Source.fromResource:

scala.io.Source.fromResource("located_in_resouces.any")
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Nik Kashi
  • 4,447
  • 3
  • 40
  • 63
21

One-liner solution for Scala >= 2.12

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

Important taken from comments (thanks to @anentropic): with Source.fromResource you do not put the initial forward slash.

freedev
  • 25,946
  • 8
  • 108
  • 125
  • 1
    just repeating here a useful comment that helped me, by @vossad01 on another answer below: "Important: with `Source.fromResource` you do not put the initial forward slash that you have with `getResourceAsStream`" – Anentropic May 09 '22 at 11:58
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))

  }

}
Sri
  • 491
  • 5
  • 9
  • 1
    When you copy from a website, please post a link to the original author. Give credit where it is due. Refer : http://fruzenshtein.com/scala-working-with-resources-folders-files/ – ForeverLearner Jul 11 '19 at 13:08
  • when the code is same it doesn't mean copied right. – Sri Aug 27 '20 at 05:25
4

The required file can be accessed as below from resource folder in scala

val file = scala.io.Source.fromFile(s"src/main/resources/app.config").getLines().mkString
Prasad Khode
  • 6,602
  • 11
  • 44
  • 59
Dileep Dominic
  • 499
  • 11
  • 23
3

For Scala 2.11, if getLines doesn't do exactly what you want you can also copy the a file out of the jar to the local file system.

Here's a snippit that reads a binary google .p12 format API key from /resources, writes it to /tmp, and then uses the file path string as an input to a spark-google-spreadsheets write.

In the world of sbt-native-packager and sbt-assembly, copying to local is also useful with scalatest binary file tests. Just pop them out of resources to local, run the tests, and then delete.

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}")
Tony Fraser
  • 727
  • 7
  • 14
0

The "resources" folder must be under the source root. if using intellj check for the blue folder in the project folders on the left side. eg AppName/src/main/scala or Project/scala/../main/ etc.

If using val stream: InputStream = getClass.getResourceAsStream("/readme.txt") don't forget the "/" (forward slash), given readme.txt is the file inside resources

ss301
  • 514
  • 9
  • 22