在Spark上运行Tika的类路径问题

时间:2015-12-15 15:25:53

标签: scala jar apache-spark classpath apache-tika

我尝试在Tika中处理一堆文件。文件数量为数千,因此我决定构建一个RDD文件,让Spark分配工作量。不幸的是,我得到了多个NoClassDefFound例外。

这是我的sbt文件:

name := "TikaFileParser"
version := "0.1"
scalaVersion := "2.11.7"

libraryDependencies += "org.apache.spark" %% "spark-core" % "1.5.1" % "provided"
libraryDependencies += "org.apache.tika" % "tika-core" % "1.11"
libraryDependencies += "org.apache.tika" % "tika-parsers" % "1.11"
libraryDependencies += "org.apache.hadoop" % "hadoop-client" % "2.7.1" % "provided"

这是我的assembly.sbt

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.1")

这是源文件:

import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
import org.apache.spark.input.PortableDataStream
import org.apache.tika.metadata._
import org.apache.tika.parser._
import org.apache.tika.sax.WriteOutContentHandler
import java.io._

object TikaFileParser {

  def tikaFunc (a: (String, PortableDataStream)) = {

    val file : File = new File(a._1.drop(5))
    val myparser : AutoDetectParser = new AutoDetectParser()
    val stream : InputStream = new FileInputStream(file)
    val handler : WriteOutContentHandler = new WriteOutContentHandler(-1)
    val metadata : Metadata = new Metadata()
    val context : ParseContext = new ParseContext()

    myparser.parse(stream, handler, metadata, context)

    stream.close

    println(handler.toString())
    println("------------------------------------------------")
  }


  def main(args: Array[String]) {

    val filesPath = "/home/user/documents/*"
    val conf = new SparkConf().setAppName("TikaFileParser")
    val sc = new SparkContext(conf)
    val fileData = sc.binaryFiles(filesPath)
    fileData.foreach( x => tikaFunc(x))
  }
}

我用

运行它
spark-submit --driver-memory 2g --class TikaFileParser --master local[4]
             /path/to/TikaFileParser-assembly-0.1.jar

获取java.lang.NoClassDefFoundError: org/apache/cxf/jaxrs/ext/multipart/ContentDisposition这是解析器的依赖关系。出于好奇,我将包含此类的jar添加到Spark的--jars选项中并再次运行。这次我得到了一个新的NoClassDefFoundError(不记得哪一个,还有Tika依赖)。

我已经在这里发现了一个类似的问题(Apache Tika 1.11 on Spark NoClassDeftFoundError),其中解决方案是构建一个胖罐。但我想知道是否还有其他方法来解决依赖问题?

顺便说一句:我在没有Spark的情况下尝试了这个片段(所以只需使用带有文件名的数组和一个foreach循环,并相应地更改了tikaFunc签名。我没有任何参数运行它,它运行得很好。

编辑:立即更新片段以用于sbt程序集。

3 个答案:

答案 0 :(得分:2)

  

我已经在这里发现了一个类似的问题(关于Spark NoClassDeftFoundError的Apache Tika 1.11)解决方案是构建一个胖罐。但我想知道是否还有其他方法来解决依赖问题?

查找所有依赖项并将其添加到--jars。您可以使用https://github.com/jrudolph/sbt-dependency-graph执行此操作。但是我不明白为什么你更喜欢用它来组建一个罐子。

  

我在没有任何争论的情况下运行它并且效果很好。

SBT已经确保您拥有类路径的所有依赖项,但Spark并没有使用SBT来运行您的程序。

答案 1 :(得分:1)

问题来自于罐子中的版本不匹配。我决定使用以下sbt文件来解决我的问题:

name := "TikaFileParser"
version := "0.1"
scalaVersion := "2.11.7"

libraryDependencies += "org.apache.spark" %% "spark-core" % "1.5.1" % "provided"
libraryDependencies += "org.apache.tika" % "tika-core" % "1.11"
libraryDependencies += "org.apache.tika" % "tika-parsers" % "1.11"
libraryDependencies += "org.apache.hadoop" % "hadoop-client" % "2.7.1" % "provided"

mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
  {
    case PathList("META-INF", xs @ _*) => MergeStrategy.discard
    case _     => MergeStrategy.first
  }
}

答案 2 :(得分:1)

我想纠正@flowit的答案,因为它使我陷入漫长的调查之中。

答案的问题是合并策略,该策略将丢弃每个META-INF目录。但是,这也将摆脱Tika正在注册i.a的META-INF/services目录。它的解析器。

使用合并策略,您可以在已接受的答案中或在周围飞来飞去的其他Stackoverflow答案中找到合并策略,最终将得到空内容,因为Tika将默认为EmptyParser。因此,如果您尝试解析任何内容,Tika将无法解析解析器。参见https://tika.apache.org/1.21/configuring.html#Static

对我来说,解决方案是(我猜是使用较新的sbt语法):

assemblyMergeStrategy in assembly := {
  case PathList("META-INF", xs @ _*) =>
    (xs map {_.toLowerCase}) match {
      case "services" :: xs => MergeStrategy.concat // Tika uses the META-INF/services to register its parsers statically, don't discard it
      case _ => MergeStrategy.discard
    }
  case x => MergeStrategy.first
}