如何从jar中的属性构造HDFS的路径?

时间:2017-10-15 15:44:22

标签: java hadoop apache-spark

我有一个小火花程序使用Scala,我想把它打包成一个可执行的fat-jar,配置在文件中设置:src/main/resource/localconfig.properties,所以我在{{1}中新建了org.apache.hadoop.fs.Path(String pathString)的实例}}:

src/main/scala/com.let.App

问题是它可以在IDEA上运行但在jar中使用val Path = new Path("localconfig.properties") 运行时失败,提示:找不到文件。

我提取jar并且属性文件位于根文件夹中:myapp,我也尝试了java -jar myapp.jar,但它也不起作用。

如何在可执行jar中为Path设置正确的路径?

这是windows环境,我读了它似乎与OS有关的Path()方法,但我仍然不知道如何使用构造函数。

2 个答案:

答案 0 :(得分:0)

引用Path(String pathString)

  

从String构造路径。路径字符串是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

答案 1 :(得分:0)

我不确定它是否仍然打开,但是在尝试访问Spark应用程序的参考数据时遇到了这个问题。 Jacek的答案很接近,但遇到了一个常见的hadoop问题,即路径无法处理多个:分隔方案,因此jar:file:仅映射到jar:下的hadoop.fs.Path上。这通常表现为有关相对路径的错误。

我发现的唯一解决方法是以InputStream的形式访问jar资源,然后将该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)