我尝试在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程序集。
答案 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
}