无法使用 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 的方法:
- 用 7-zip
打开“tika-app-1.28.2.jar”
- 打开 META-INF 然后 select MANIFEST.MF 然后按 F4 在文本编辑器中打开它
- 将以下内容添加到文件末尾:
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
- 提示时保存选择更新。
然后您可以使用 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,因此无法在上下文中对其进行测试。
我想通过 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 的方法:
- 用 7-zip 打开“tika-app-1.28.2.jar”
- 打开 META-INF 然后 select MANIFEST.MF 然后按 F4 在文本编辑器中打开它
- 将以下内容添加到文件末尾:
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
- 提示时保存选择更新。
然后您可以使用 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,因此无法在上下文中对其进行测试。