无法使用 Lucee 通过 Apache Tika 提取文本

Can not extract text via Apache Tika using Lucee

我想通过 Lucee 5+ (5.2.9) 从 pdf、docx 等文件中提取文本,但不幸的是我得到的结果集为空。我使用了几个可能适合我的特定 Lucee 和 Java 要求的 Apache Tika versions(运行nable jar with Java 1.8.0),但结果集始终为空。

exract.cfc

component {
    
    public any function init() {

        _setTikaJarPath( GetDirectoryFromPath( GetCurrentTemplatePath( ) ) & "tika-app-1.19.1.jar" );

        return this;

    }


    private struct function doParse( required any fileContent, boolean includeMeta=true, boolean includeText=true ) {
        var result  = {};
        var is      = "";
        var jarPath = _getTikaJarPath();

        if ( IsBinary( arguments.fileContent ) ) {
            is = CreateObject( "java", "java.io.ByteArrayInputStream" ).init( arguments.fileContent );
        } else {
            // TODO, support plain string input (i.e. html)
            return {};
        }

        try {
            var parser = CreateObject( "java", "org.apache.tika.parser.AutoDetectParser", jarPath );
            var ch     = CreateObject( "java", "org.apache.tika.sax.BodyContentHandler" , jarPath ).init(-1);
            var md     = CreateObject( "java", "org.apache.tika.metadata.Metadata"      , jarPath ).init();

            parser.parse( is, ch, md );

            if ( arguments.includeMeta ) {
                result.metadata = {};

                for( var key in md.names() ) {
                    var mdval = md.get( key );
                    if ( !isNull( mdval ) ) {
                        result.metadata[ key ] = _removeNonUnicodeChars( mdval );
                    }
                }
            }

            if ( arguments.includeText ) {
                result.text = _removeNonUnicodeChars( ch.toString() );
            }

        } catch( any e ) {
            result = { error = e };
        }

        return result;
}


    public function read(required string filename) {
        var result = {};

        if(!fileExists(filename)) {
            result.error = "#filename# does not exist.";
            return result;
        };

        var f = createObject("java", "java.io.File").init(filename);
        var fis = createObject("java","java.io.FileInputStream").init(f);

        try {
            result = doParse(fis);
        } catch(any e) {
            result.error = e;
        }
        fis.close();
        return result;
    }

    private string function _removeNonUnicodeChars( required string potentiallyDirtyString ) {
        return ReReplace( arguments.potentiallyDirtyString, "[^\x20-\x7E]", "", "all" );
    }

// GETTERS AND SETTERS
    private string function _getTikaJarPath() {
        return _tikaJarPath;
    }
    private void function _setTikaJarPath( required string tikaJarPath ) {
        _tikaJarPath = arguments.tikaJarPath;
}


}

以及我用来 运行 的代码

<cfset takis = new exract()>
<cfset files = directoryList(expandPath("./sources"))>
<cfloop index="f" array="#files#">
    <cfif not findNoCase(".DS_Store",f)>
        <cfdump var="#takis.read(f)#" label="#f#">
    </cfif>
</cfloop>

我认为问题是 class 冲突:Lucee 核心引擎已经加载了一个版本的 Tika,这意味着您指向的版本被忽略了。但是加载的版本没有按预期运行,如您所见返回空字符串。

我已经通过使用 OSGi 加载所需的 Tika 版本解决了这个问题。这涉及编辑 tika-app jar 的清单以包含基本的 OSGi 元数据,然后通过我的 osgiLoader

加载它

有一个 pre-built Tika bundle 可用,但我无法让它与 Lucee 一起使用。

以下是将最新的 tika-app jar 转换为 OSGi 的方法:

  1. 用 7-zip
  2. 打开“tika-app-1.28.2.jar”
  3. 打开 META-INF 然后 select MANIFEST.MF 然后按 F4 在文本编辑器中打开它
  4. 将以下内容添加到文件末尾:
Bundle-Name: Apache Tika App Bundle
Bundle-SymbolicName: apache-tika-app-bundle
Bundle-Description: Apache Tika App jar converted to an OSGi bundle
Bundle-ManifestVersion: 2
Bundle-Version: 1.28.2
Bundle-ClassPath: .,tika-app-1.28.2.jar
  1. 提示时保存选择更新。

然后您可以使用 osgiLoader 调用 jar,如下所示:

extractor.cfc

component{

    property name="loader" type="object";
    property name="tikaBundle" type="struct";

    public extractor function init( required object loader, required struct tikaBundle ){
        variables.loader = arguments.loader
        variables.tikaBundle = arguments.tikaBundle
        return this
    }

    public string function parseToString( required string filePath ){
        try{
            var fileStream = CreateObject( "java", "java.io.FileInputStream" ).init( JavaCast( "string", arguments.filePath ) )
            var tikaObject = loader.loadClass( "org.apache.tika.Tika", tikaBundle.path, tikaBundle.name, tikaBundle.version )
            var result = tikaObject.parseToString( fileStream )
        }
        finally{
            fileStream.close()
        }
        return result
    }

}

(以下脚本假定extractor.cfc,修改后的Tika jar,osgiLoader.cfc和待处理的文档在同一目录下。)

index.cfm

<cfscript>
docPath = ExpandPath( "test.pdf" )
loader = New osgiLoader()
tikaBundle = {
    version: "1.28.2"
    ,name: "apache-tika-app-bundle"
    ,path: ExpandPath( "tika-app-1.28.2.jar" )
}
extractor = New extractor( loader, tikaBundle )
result = extractor.parseToString( docPath )
dump( result )
</cfscript>

另一种加载正确版本的方法是使用 JavaLoader。出于某种原因,我无法使用最新的 tika-app jar (1.28.2),但 1.19.1 似乎可以工作。

破解现有扩展程序

我建议您向 Preside 提出问题以更改其扩展名以避免冲突,但作为临时 hack,您可以尝试自己修改如下:

首先,将修改后的 Tika 包和 osgiLoader.cfc 添加到 /preside-ext-tika/services/ 目录。

接下来,更改 DocumentMetadataService.cfc 的第 14 行,使 Tika jar 路径的名称与您修改后的包匹配。

_setTikaJarPath( GetDirectoryFromPath( GetCurrentTemplatePath( ) ) & "tika-app-1.28.2.jar" );

然后,修改同一个cfc的第33-35行,替换为:

var parser = CreateObject( "java", "org.apache.tika.parser.AutoDetectParser", jarPath );
var ch     = CreateObject( "java", "org.apache.tika.sax.BodyContentHandler" , jarPath ).init(-1);
var md     = CreateObject( "java", "org.apache.tika.metadata.Metadata"      , jarPath ).init();

具有以下内容:

var loader = New osgiLoader();
var tikaBundle = { version: "1.28.2", name: "apache-tika-app-bundle" };

var parser = loader.loadClass( "org.apache.tika.parser.AutoDetectParser", jarPath, tikaBundle.name, tikaBundle.version )
var ch     = loader.loadClass( "org.apache.tika.sax.BodyContentHandler" , jarPath, tikaBundle.name, tikaBundle.version ).init(-1)
var md     = loader.loadClass( "org.apache.tika.metadata.Metadata"      , jarPath, tikaBundle.name, tikaBundle.version ).init()

注意:我没有 Preside,因此无法在上下文中对其进行测试。