类路径问题 运行 Tika on Spark

Classpath issues running Tika on Spark

我尝试在 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 这是解析器的依赖项。出于好奇,我将包含此 class 的 jar 添加到 Spark 的 --jars 选项并再次添加 运行。这次又得到了一个新的NoClassDefFoundError(记不清是哪一个了,也是个Tika依赖)

我已经在此处 (Apache Tika 1.11 on Spark NoClassDeftFoundError) 发现了一个类似的问题,解决方案是构建一个 fat jar。但是我想知道有没有其他方法可以解决依赖问题?

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

编辑:更新了现在用于 sbt 程序集的片段。

I already found a similar problem here (Apache Tika 1.11 on Spark NoClassDeftFoundError) where the solution was to build a fat jar. But I would like to know if there is any other way so solve the dependency issues?

找到所有依赖项并将它们添加到--jars。您可以使用 https://github.com/jrudolph/sbt-dependency-graph 来完成。但我不明白为什么你更喜欢这个而不是构建一个将它们组合在一起的罐子。

I ran it without any arguments and it worked perfectly.

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

这些问题是由于 jars 中的版本不匹配造成的。我决定使用以下 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
  }
}

我想更正@flowit 的答案,因为它让我陷入了漫长的一天调查。

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

使用合并策略(您可以在已接受的答案或其他随处可见的 Whosebug 答案中找到),您最终将得到空内容,因为 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
}