2

我正在尝试解析 mbox 格式的电子邮件。但是,Tika 不断尝试在这些消息上使用 TNEFParser 导致错误:

2012-08-21 17:44:42,139 FATAL org.apache.hadoop.mapred.Child: Error running child : java.lang.OutOfMemoryError: Java heap space
    at org.apache.poi.hmef.attribute.TNEFAttribute.<init>(TNEFAttribute.java:50)
    at org.apache.poi.hmef.attribute.TNEFAttribute.create(TNEFAttribute.java:76)
    at org.apache.poi.hmef.HMEFMessage.process(HMEFMessage.java:74)
    at org.apache.poi.hmef.HMEFMessage.process(HMEFMessage.java:98)
    at org.apache.poi.hmef.HMEFMessage.process(HMEFMessage.java:98)
    at org.apache.poi.hmef.HMEFMessage.<init>(HMEFMessage.java:63)
    at org.apache.tika.parser.microsoft.TNEFParser.parse(TNEFParser.java:80)
    at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:242)
    at org.apache.tika.parser.mail.MailContentHandler.body(MailContentHandler.java:102)
    at org.apache.james.mime4j.parser.MimeStreamParser.parse(MimeStreamParser.java:133)
    at org.apache.tika.parser.mail.RFC822Parser.parse(RFC822Parser.java:76)
    at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:242)
    at org.apache.tika.parser.CompositeParser.parse(CompositeParser.java:242)
    at org.apache.tika.parser.AutoDetectParser.parse(AutoDetectParser.java:120)
    at org.lab41.asf.etl.mapred.MailboxToTextMapper.parse(MailboxToTextMapper.java:124)
    at org.lab41.asf.etl.mapred.MailboxToTextMapper.map(MailboxToTextMapper.java:88)
    at org.lab41.asf.etl.mapred.MailboxToTextMapper.map(MailboxToTextMapper.java:45)
    at org.apache.avro.mapred.HadoopMapper.map(HadoopMapper.java:81)
    at org.apache.avro.mapred.HadoopMapper.map(HadoopMapper.java:34)
    at org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:50)
    at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:391)
    at org.apache.hadoop.mapred.MapTask.run(MapTask.java:325)
    at org.apache.hadoop.mapred.Child$4.run(Child.java:266)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:396)
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1278)
    at org.apache.hadoop.mapred.Child.main(Child.java:260)

是否可以阻止 Tika 使用 TNEFParser?任何的意见都将会有帮助。

4

3 回答 3

5

这是@Gagravarr 建议的配置版本。

首先,创建一个 tika-config.xml 文件:

<properties>
  <parsers>

    <!-- use the default parser in most cases, it is a composite of all 
         the parsers listed in META-INF/services/org.apache.tika.parser.Parser -->
    <parser class="org.apache.tika.parser.DefaultParser"/>

    <!-- Disable tnef extraction-->    
    <parser class="org.apache.tika.parser.EmptyParser">
      <mime>application/vnd.ms-tnef</mime>
      <mime>application/x-tnef</mime>
    </parser>

  </parsers>
</properties>

现在,从这个配置创建一个 TikaConfig(假设它在你的类路径的某个地方):

ClassLoader loader = Thread.currentThread().getContextClassLoader();
TikaConfig config = new TikaConfig(loader.getResource("tika-config.xml"), loader);

当你创建一个新的 Parser,或者使用 Tika 门面时,传入你的配置:

AutoDetectParser parser = new AutoDetectParser(config);
ParseContext context = new ParseContext();
context.set(Parser.class, parser);
parser.parse(input, handler, metadata, context);

任何标识为 TNEF 的文档都将使用 EmptyParser,它不返回任何内容并且实际上不解析任何内容。

这实际上是一个黑名单,如果您想要一个白名单,您需要从 XML 中删除 DefaultParser 并手动配置每个解析器及其元数据。

于 2013-01-30T05:06:59.230 回答
3

对于长期修复,您应该将其报告为 Apache Tika 中的错误,将有问题的文件附加到错误报告中,并与项目一起修复错误。

短期而言,解压 Tika-Parsers jar 文件,编辑META-INF/services/org.apache.tika.parser.Parser文件并从列表中删除 TNEF 解析器。这将阻止它被 AutoDetectParser 自动加载和使用

如果不更改 Tika Parsers jar 文件,这有点棘手。有两种选择。一种是自己创建一个 TikaConfig 实例,而不是依赖默认实例,并且只提供有限的解析器列表。根据您是要列入白名单还是列入黑名单,这可能更容易或更困难。或者,您可以使用最后注册的 mimetype 解析器获胜的事实。因此,使用服务文件和您自己的虚拟解析器创建您自己的 jar。让该解析器声明它处理 TNEF mimetype,但让它什么都不做。将 jar 添加到您的类路径中,然后将使用您的虚拟解析器

于 2012-08-22T10:08:31.607 回答
3

这是@Gagravarr建议的程序化版本。它将注册的不必要的解析器替换为EmptyParser.

private Tika createTika(final Parser... unnecessaryParsers) 
        throws TikaException, IOException {
    final TikaConfig config = new TikaConfig();
    final AutoDetectParser autoDetectParser = new AutoDetectParser(config);

    final Set<MediaType> unnecessaryMimeTypes = 
        getUnnecessaryMediaTypes(unnecessaryParsers);
    disableParsing(autoDetectParser, unnecessaryMimeTypes);

    final Detector detector = config.getDetector();
    final Tika tika = new Tika(detector, autoDetectParser);
    return tika;
}

private Set<MediaType> getUnnecessaryMediaTypes(
        final Parser... unnecessaryParsers) {
    final Set<MediaType> unnecessaryTypes = new HashSet<MediaType>();
    for (final Parser unnecessaryParser: unnecessaryParsers) {
        final Set<MediaType> supportedTypes = 
            unnecessaryParser.getSupportedTypes(null);
        unnecessaryTypes.addAll(supportedTypes);
    }
    return unnecessaryTypes;
}

private void disableParsing(final CompositeParser mainParser, 
        final Set<MediaType> unnecessaryMediaTypes) {
    final EmptyParser emptyParser = new EmptyParser();

    final Map<MediaType, Parser> parsers = mainParser.getParsers();
    for (final MediaType unnecessaryType: unnecessaryMediaTypes) {
        parsers.put(unnecessaryType, emptyParser);
    }

    mainParser.setParsers(parsers);
}

用法:

final Parser unnecessaryParser = new MP4Parser();
final Tika tika = createTika(unnecessaryParser);

您也可以使用它来避免TIKA-1040: Could not delete temporary file

于 2013-01-19T17:23:09.590 回答