我有一个使用 Scala 的小火花程序,我想把它打包到一个可执行的 fat-jar 中,配置文件中设置的配置:src/main/resource/localconfig.properties
,所以我在 src/main/scala/com.let.App
中新建了一个 org.apache.hadoop.fs.Path(String pathString)
实例:
val Path = new Path("localconfig.properties")
问题是它可以在 IDEA 上运行,但当打包在 jar 中并以 java -jar myapp.jar
运行时失败,提示:找不到文件。
我提取jar,属性文件位于根文件夹中:myapp,我也尝试过Path("resources/localconfig.properties")
,但它也不起作用。
如何在可执行 jar 中为路径设置正确的路径?
这是Windows环境,我阅读了Path()方法,它似乎与操作系统有关,但我仍然不知道如何使用构造函数。
引用路径(字符串路径字符串):
从字符串构造路径。路径字符串是 URI,但具有未转义的元素和一些额外的规范化。
这意味着pathString
(例如 localconfig.properties
在您的示例中)应该在文件系统上可用,但显然并非如此。该文件位于 jar 文件中,因此您应该让 JVM 为您提供该文件。
这就是你应该使用Hadoop HDFS的Path(URI aUri)和Java的ClassLoader.getResource方法的地方。
$ jar cvf /tmp/hello.jar hello.xml
$ jar -tf /tmp/hello.jar
META-INF/
META-INF/MANIFEST.MF
hello.xml
// start spark-shell with the jar on the CLASSPATH
// that should be exactly your case where your jar is on CLASSPATH
// with the file you want to use to construct Path inside
$ ./bin/spark-shell --jars /tmp/hello.jar
scala> this.getClass.getResource("/hello.xml")
res0: java.net.URL = jar:file:/tmp/hello.jar!/hello.xml
// Convert URL to URI that Path supports
scala> this.getClass.getResource("/hello.xml").toURI
res1: java.net.URI = jar:file:/tmp/hello.jar!/hello.xml
scala> import org.apache.hadoop.fs.Path
import org.apache.hadoop.fs.Path
scala> new Path(this.getClass.getResource("/hello.xml").toURI)
res3: org.apache.hadoop.fs.Path = jar:
但是,我不知道是否支持构建这样的Path
。
我不确定这是否仍然打开,但我在尝试访问 Spark 应用程序的参考数据时遇到了这个问题。 Jacek 的答案很接近,但它遇到了一个常见的 Hadoop问题,即路径无法处理多个:
分隔方案,因此jar:file:
被映射到hadoop.fs.Path
下的jar:
。这通常表现为有关相对路径的错误。
我发现的唯一解决方法是将 jar 资源作为InputStream
访问,然后将该InputStream
复制到临时(本地)文件并从那里加载它。 这不是超级高效,也不是特别漂亮,但它似乎有效(我相信其他人会遇到写入权限问题,但我还没有)。
import org.apache.commons.io.FileUtils
val resourceName: String = "myResource.tsv"
val inputStream: InputStream = YourClass.getClass.getResourceAsStream(resourceName)
val tmpDir: String = FileUtils.getTempDirectoryPath // You need write permissions here
val outPath: java.nio.file.Path = java.nio.file.Paths.get(tmpDir, resourceName)
val outUriString: String = outPath.toUri.toString // Need full URI
val java.nio.file.Files.copy(inputStream, outPath) // Signature requires (InputStream, Path)
new java.io.File(outUriString).deleteOnExit() // Delete temp file after execution
/* You should be able to access the local file now, e.g. */
val data = spark.read.csv(outUriString)