Golang调用Apache Tika服务解析PDF文档的示例教程

98 阅读6分钟

Golang调用Apache Tika服务解析PDF文档的示例教程

本文档将详细介绍如何使用github.com/google/go-tika/tika库来处理PDF文档。内容包括tika-server-standard-2.1.0.jar的介绍、下载、启动方法,端口检测,以及如何通过Go语言调用服务上传PDF文件并接收返回的文本内容。

1. Apache Tika简介

Apache Tika是一个内容提取工具,可以从各种文档格式中提取文本和元数据。Tika Server是Tika的服务器版本,可以通过HTTP API进行调用。go-tika是Google提供的Go语言客户端库,用于与Tika Server进行交互。

1.1 tika-server-standard-2.1.0.jar介绍

tika-server-standard-2.1.0.jar是Apache Tika 2.1.0版本的服务端程序,提供了RESTful API接口,允许通过HTTP请求来处理各种文档格式。在本项目中,我们使用这个版本来解析PDF文档。

1.2 下载tika-server-standard-2.1.0.jar

可以从Apache Tika官网或镜像站点下载tika-server-standard-2.1.0.jar文件:

https://archive.apache.org/dist/tika/2.1.0/tika-server-standard-2.1.0.jar

在本项目中,该文件已经包含在java-1.8.0目录中。

2. 启动Tika服务

2.1 通过命令行启动Tika服务

要启动Tika服务,需要确保系统已安装Java 8或更高版本。然后在命令行中执行以下命令:

java -jar tika-server-standard-2.1.0.jar

在本项目中,可以通过以下命令启动:

java -jar java-1.8.0/tika-server-standard-2.1.0.jar

默认情况下,Tika服务会在本地9998端口启动。

2.2 在Go程序中启动Tika服务

项目中提供了通过Go程序启动Tika服务的方法:

package main

import (
	"fmt"        // 用于格式化输出
	"os"         // 用于操作系统相关功能
	"os/exec"    // 用于执行外部命令
	"path/filepath" // 用于处理文件路径
	"time"       // 用于时间处理
)

// startTikaServer 通过Java命令启动Tika服务
// 该函数会获取当前目录,构造Java命令并执行启动Tika服务
func startTikaServer() {
	// 获取当前工作目录的绝对路径
	// filepath.Abs函数会返回指定路径的绝对路径
	dir, _ := filepath.Abs(`.`)
	
	// 构造启动Tika服务的命令字符串
	// 使用项目中自带的Java运行时和Tika服务jar包
	cmdStr := dir + "/java-1.8.0/jre/bin/java -jar " + dir + "/java-1.8.0/tika-server-standard-2.1.0.jar"
	
	// 打印将要执行的命令,便于调试
	fmt.Println(cmdStr)
	
	// 使用exec.Command创建一个命令对象
	// cmd.exe是Windows命令行解释器
	// /c参数表示执行完命令后关闭命令行窗口
	// start参数用于在新窗口中启动程序
	c := exec.Command("cmd.exe", "/c", "start "+cmdStr)

	// 执行命令,如果出错则打印错误信息
	if err := c.Run(); err != nil {
		fmt.Println("Error: ", err)
	}
	
	// 等待一段时间确保服务启动完成
	time.Sleep(10 * time.Second)
}

3. 端口检测

在启动Tika服务之前,我们需要检测Tika服务端口(默认9998)是否已经被占用。

package main

import (
	"fmt"     // 用于格式化输出
	"net"     // 用于网络操作
	"time"    // 用于时间处理
)

// IsPortInUse 检查指定端口是否被占用
// 参数:
//   - port: 要检查的端口号
// 返回值:
//   - bool: 如果端口被占用返回true,否则返回false
func IsPortInUse(port string) bool {
	// 使用net.DialTimeout尝试连接到指定端口
	// 参数说明:
	//   - "tcp": 使用TCP协议
	//   - port: 目标地址和端口,格式为"host:port"
	//   - 3*time.Second: 连接超时时间设置为3秒
	conn, err := net.DialTimeout("tcp", port, 3*time.Second)
	
	// 如果连接出错,说明端口未被占用
	if err != nil {
		return false
	}
	
	// 如果连接成功,关闭连接并返回true
	if conn != nil {
		conn.Close()
		return true
	}
	
	// 默认返回false
	return false
}

// CheckTikaServerPort 检查Tika服务端口是否可用
// 如果端口不可用,则启动Tika服务
func CheckTikaServerPort() {
	// 定义Tika服务的默认端口
	port := "127.0.0.1:9998"
	
	// 获取当前时间并格式化,用于日志输出
	now := time.Now().Format("2006-01-02 15:04:05")
	
	// 检查端口是否被占用
	conn, err := net.DialTimeout("tcp", port, 3*time.Second)
	
	// 如果连接出错,说明端口未开启
	if err != nil {
		// 打印端口未开启的信息
		fmt.Println("["+now+"]", port, "端口未开启(fail)!")
		// 打印正在启动端口的信息
		fmt.Println("["+now+"]", port, "端口正在启动,请稍后...")
		
		// 调用startTikaServer函数启动Tika服务
		startTikaServer()
		
		// 等待20秒确保服务启动完成
		time.Sleep(20 * time.Second)
		
		// 打印端口已开启的信息
		fmt.Println("["+now+"]", port, "端口已开启(success)!")
	} else {
		// 如果连接成功,说明端口已开启
		if conn != nil {
			// 打印端口已开启的信息
			fmt.Println("["+now+"]", port, "端口已开启(success)!")
			// 关闭连接
			conn.Close()
		} else {
			// 如果连接对象为空,打印端口未开启的信息
			fmt.Println("["+now+"]", port, "端口未开启(fail)!")
		}
	}
}

4. 使用go-tika库解析PDF文档

4.1 导入必要的包

package main

import (
	"context"  // 用于控制请求的上下文
	"fmt"      // 用于格式化输出
	"log"      // 用于记录日志
	"os"       // 用于文件操作
	
	// 导入go-tika库,用于与Tika服务交互
	"github.com/google/go-tika/tika"
)

4.2 创建Tika客户端并解析PDF

// readPdfTika 使用Tika服务解析PDF文件
// 参数:
//   - pdfFileName: PDF文件的路径
// 返回值:
//   - string: 解析后的文本内容
//   - error: 错误信息(如果有)
func readPdfTika(pdfFileName string) (string, error) {
	// 打开指定路径的PDF文件
	// os.Open函数会返回一个文件对象和可能的错误信息
	f, err := os.Open(pdfFileName)
	
	// 如果打开文件时出错,记录错误日志并返回
	if err != nil {
		log.Fatal(err)
		return "", err
	}
	
	// 使用defer确保在函数结束时关闭文件
	// defer语句会延迟执行,确保即使出现错误也能正确关闭文件
	defer f.Close()

	// 打印正在处理的文件名
	fmt.Println(f.Name())

	// 创建Tika客户端
	// 第一个参数是HTTP客户端,传入nil表示使用默认的HTTP客户端
	// 第二个参数是Tika服务的地址,这里使用本地9998端口
	client := tika.NewClient(nil, "http://localhost:9998")
	
	// 调用Tika服务解析PDF文件
	// client.Parse方法会向Tika服务发送解析请求
	// context.Background()提供一个空的上下文
	// f是之前打开的文件对象
	body, err := client.Parse(context.Background(), f)
	
	// 打印网络错误信息(如果有的话)
	fmt.Printf("->网络错误:%v\n", err)

	// 打印解析结果
	fmt.Println(body)

	// 返回解析结果和错误信息
	return body, err
}

4.3 完整的使用示例

func main() {
	// 检查Tika服务端口,如果未开启则启动服务
	CheckTikaServerPort()
	
	// 指定要解析的PDF文件路径
	pdfFile := "example.pdf"
	
	// 调用readPdfTika函数解析PDF文件
	content, err := readPdfTika(pdfFile)
	
	// 检查是否有错误
	if err != nil {
		// 如果有错误,打印错误信息
		fmt.Printf("解析PDF文件时出错: %v\n", err)
		return
	}
	
	// 打印解析结果
	fmt.Println("PDF文件解析结果:")
	fmt.Println(content)
}

5. 高级用法

5.1 使用Tika客户端的其他方法

go-tika库还提供了其他有用的方法:

// 使用Meta方法提取文档元数据
meta, err := client.Meta(context.Background(), f)
if err != nil {
    log.Fatal(err)
}
fmt.Println("文档元数据:", meta)

// 使用Detect方法检测文档类型
mimeType, err := client.Detect(context.Background(), f)
if err != nil {
    log.Fatal(err)
}
fmt.Println("文档类型:", mimeType)

// 使用Language方法检测文档语言
language, err := client.Language(context.Background(), content)
if err != nil {
    log.Fatal(err)
}
fmt.Println("文档语言:", language)

5.2 处理解析结果

通常需要对Tika返回的文本进行进一步处理:

// 处理解析结果,去除多余空白字符
func processParsedContent(content string) string {
	// 将换行符替换为分号,便于后续处理
	content = strings.Replace(content, "\r\n", ";", -1)
	
	// 去除HTML标签
	re := regexp.MustCompile(`<[^>]*>`)
	content = re.ReplaceAllString(content, "")
	
	// 去除多余空格
	content = strings.TrimSpace(content)
	
	return content
}

6. 总结

通过本教程,我们学习了如何:

  1. 了解Apache Tika和go-tika库的基本概念
  2. 下载和启动Tika服务
  3. 在Go程序中检测端口并自动启动Tika服务
  4. 使用go-tika库解析PDF文档
  5. 处理解析结果

go-tika库提供了一种简单而有效的方式来处理各种文档格式,特别适用于需要从PDF等复杂文档中提取文本的场景。在本项目中,这个功能被用于解析各种类型的PDF看板文件,提取其中的关键信息。