JAVA实现word和pdf的数据解析(图片,表格等)转HTML存储以及海量搜索

JAVA实现word和pdf的数据解析(图片,表格等)转HTML存储以及海量搜索

在这里插入图片描述

业务需求:需求方有大量的word和pdf文件,每次找资料电子手册很麻烦,需要做一个导入文件后根据关键字搜索(类似于Baidu)查看文件的功能,详情有左侧文件目录 右侧文件详细内容
一、java代码层面的解析

导入文件把文件转html数据

实现文件转html并解析表格等需要一个Jacob的jar包和dll文件 配置

链接:https://pan.baidu.com/s/1IZPtVomOIWdDWqD4XkPSlw
提取码:jayj

  1. 下载JACOB的JAR和DLL文件

    • 从JACOB的官方网站或上面我提供的网盘下载最新的JAR和DLL文件。

      个人选择了比较新版本的jacob的jar包和dll文件 可以下面自行下载或者去jacob官网下载

  2. 将JAR文件添加到项目的libs目录中

    • 在你的Gradle项目中创建一个libs目录(如果还没有的话)。
    • 将下载的JAR文件复制到libs目录中。
  3. 在Gradle构建脚本中添加JAR作为依赖
    编辑你的build.gradle文件,并在dependencies部分添加对JAR文件的引用

    这个是gradle的,如果是maven自行转一下

  4. 配置DLL文件的路径

    • 由于JACOB包含本地代码(DLL文件),你需要确保Java能够找到它。你可以将DLL文件放在Java的系统路径上也就是你的jdk文件的bin下面,或者在你的Java代码中通过System.setProperty("java.library.path", "<path_to_dll>")来设置它。
  5. 同步Gradle项目

    • 在IDE中(如IntelliJ IDEA或Android Studio),执行Gradle同步操作,以便IDE能够识别新的依赖。
  6. 编写代码以使用JACOB
    一旦JAR文件被添加到项目中,你就可以像你在示例中那样使用ActiveXComponent类来启动Word并与之交互了

    dependencies {  
        // ... 其他依赖 ...
        // 添加jsoup的依赖
        implementation 'org.jsoup:jsoup:1.17.2'
        // 添加JACOB的依赖
        implementation files('libs/jacob.jar') // 假设你的JAR文件名为jacob.jar  
    }
    

写一个main测试一下解析的结果 以及一些业务需要的方法附上代码

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
    public static void main(String[] args) {
//
        String filepath = "C:\\Users\\Administrator\\Desktop\\文件\\";
        String htmlpath = "C:\\Users\\Administrator\\Desktop\\文件\\html\\";
        String filename = "(无水印)01.《图片定价议价规则(试行)》(国办函【2019】11号)";
        String ext = ".docx";
        try {
            filedata(filepath, filename,htmlpath,ext);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


        String htmlFilePath = "C:\\Users\\Administrator\\Desktop\\文件\\html\\(无水印)01.《图片定价议价规则(试行)》(国办函【2019】11号).html";

        String htmlString = toHtmlString(htmlFilePath);
        List<String> dlList = null;
        try {
            dlList = getDLList(htmlFilePath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        for (int i = 0; i < dlList.size(); i++) {
            System.out.println("第"+i+"条:"+dlList.get(i));
        }


        /**
         * 获取所有表格
         * */
        List<String> bgList = null;
        Elements allTable =null;
        try {
           allTable = getAllTable(htmlFilePath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        for (int i = 0; i < allTable.size(); i++) {
            System.out.println("第"+i+"条:"+allTable .get(i));
        }


    }
    public  String getRemoveTableStr(String htmlFilePath) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(htmlFilePath)),"GBK");
        String specialStr = removeContentStyle(content);
        // 使用 Jsoup 解析 HTML 字符串
        Document doc = Jsoup.parse(specialStr);
        // 获取纯文本
        String plainText = doc.text();
//		System.out.println(plainText);
        return plainText;
    }

    /**
     * 获取所有的Table
     * @param htmlFilePath
     * @return
     * @throws IOException
     * 2024年6月19日下午3:36:39
     */
    public  Elements getAllTable(String htmlFilePath) throws IOException {
        List<String> bgList = new ArrayList<>();
        // 加载 HTML 文件
        File input = new File(htmlFilePath);
        Elements tables=null;
        try {
            // 解析 HTML 文件
            Document doc = Jsoup.parse(input, null);

            // 选择所有的表格元素
            tables = doc.select("table");

            // 遍历每个表格元素
            for (Element table : tables) {
                // 获取当前表格中的所有行
                Elements rows = table.select("tr");

                //创建StringBuilder类的实例
                StringBuilder builder = new StringBuilder();
                // 遍历每行
                for (Element row : rows) {
                    // 输出行内容
//                    System.out.println("行内容:");
//                    System.out.println(row.text());
                    //将获取的text写入StringBuilder容器
                    builder.append(row.text());
                    builder.append("\r\n");
                }
//                System.out.println("-----------------------------------------------");
//                System.out.println(builder.toString());
//                System.out.println("-----------------------------------------------");
                bgList.add(builder.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tables;
    }

    /**
     * 获取以10个逗号为一个段落的list
     * @param
     * @return
     * 2024年6月19日下午2:04:17
     * @throws IOException
     */
    public  List<String> getDLList(String htmlFilePath) throws IOException {
        /**
         * 删除Table表格
         * */
        String content = getRemoveTableStr(htmlFilePath);
//		System.out.println(content);
//        String[] jhArr = content.split("。");
//        List<String> dlList = new ArrayList<String>();//10个句号为一个段落
//        String jhStrTen=new String();
//        int lastDl=0;
//        for (int i = 0; i < jhArr.length; i++) {
//        	if (i % 10 == 0 && i>0) {
//        		dlList.add(jhStrTen);
//        		if (jhArr.length-i>10) {
//        			jhStrTen="";
//				}
//        		if (jhArr.length-i<=10 && lastDl==0) {
//					lastDl = 1;
//					jhStrTen = "";
//				}
//			}else{
//				jhStrTen = jhStrTen +jhArr[i]+"。";
//				if ((i+1) == jhArr.length) {
//					dlList.add(jhStrTen);
//				}
//			}
//		}
        

        List<String> dlList = new ArrayList<>();
        int index = 0;
        while (index < content.length()) {
            int endIndex = index + 1; // 初始化结束索引
            for (int i = 0; i < 10; i++) {
                endIndex = content.indexOf("。", endIndex + 1); // 查找句号
                if (endIndex == -1) {
                    break; // 如果没有找到句号,跳出循环
                }
            }
            if (endIndex == -1) {
                endIndex = content.length(); // 如果不满十个句号,结束索引为字符串长度
            }
            dlList.add(content.substring(index, endIndex)); // 将段落添加到列表中
            index = endIndex + 1; // 更新起始索引
        }

        return dlList;
    }


    /**
     * @param
     * @param filename  不带后缀名的文件
     * @throws Exception
     * 2024年6月18日下午3:31:55
     */
    public  void filedata(String fileDir, String filename,String htmlPath,String ext) throws Exception{
        final ExecutorService exec = Executors.newFixedThreadPool(1);

        Callable<String> call = new Callable<String>() {
            public String call() throws Exception {
                //开始执行耗时操作
                // 文件路径不存在则进行创建
                File dir = new File(htmlPath);
                if (!dir.exists()) {
                    if (!dir.mkdirs()) {
                        throw new IOException("无法创建此目录: " + htmlPath);
                    }
                }
                if(ext.equals(".pdf")){
                  //pdf
                    PDFtoWord(fileDir + filename + ".PDF", htmlPath + filename + ".docx");
                    Path htmlFilePath = Paths.get(htmlPath, filename + ".html");
                    if (!Files.exists(htmlFilePath)) {
                        wordToHtml(fileDir + filename + ext, htmlPath + filename + ".html");
                    }

                }else if(ext.equals(".docx")){
                   //docx
                    Path htmlFilePath = Paths.get(htmlPath, filename + ".html");
                    if (!Files.exists(htmlFilePath)) {
                        wordToHtml(fileDir + filename + ext, htmlPath + filename + ".html");
                    }
                }
                return "线程执行完成.";
            }
        };
        try {
            Future<String> future = exec.submit(call);
            String obj = future.get(1000 * 600, TimeUnit.MILLISECONDS); //任务处理超时时间设为 1 秒
            System.out.println("文件转换:" + obj);
        } catch (Exception e) {
            //关闭Acrobat
            String command = "taskkill /f /im Acrobat.exe";
            Runtime.getRuntime().exec(command);
            throw e;
        } finally {
            // 关闭线程池
            exec.shutdown();
        }
    }


    public  boolean PDFtoWord(String source, String target) {
        ComThread.InitSTA();//初始化com的线程
        // pdfActiveX PDDoc对象 主要建立PDF对象
        ActiveXComponent app = null ;
        try {
            File inPath = new File(source);
            File outPath = new File(target);
            app = new ActiveXComponent("AcroExch.PDDoc");
            // PDF控制对象
            Dispatch pdfObject = app.getObject();
            long start = System.currentTimeMillis();
            // 打开PDF文件,建立PDF操作的开始
            Dispatch.call(pdfObject, "Open", new Variant(inPath.getAbsolutePath()));
            Variant jsObj = Dispatch.call(pdfObject, "GetJSObject");
            Dispatch.call(jsObj.getDispatch(), "SaveAs", outPath.getPath(), "com.adobe.acrobat.docx");
            app.invoke("Close");
            // 关闭PDF
            app.invoke("Close", new Variant[] {});
            long end = System.currentTimeMillis();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            ComThread.Release();//关闭com的线程   真正kill进程
        }

        return true;
    }



    public  boolean wordToHtml(String inPath, String toPath) {
        ComThread.InitSTA();//初始化com的线程
        // 启动word

        ActiveXComponent axc = new ActiveXComponent("Word.Application");

        boolean flag = false;

        try {

            // 设置word不可见

            axc.setProperty("Visible", new Variant(false));

            Dispatch docs = axc.getProperty("Documents").toDispatch();

            // 打开word文档

            Dispatch doc = Dispatch.invoke(

                    docs,

                    "Open",

                    Dispatch.Method,

                    new Object[] { inPath, new Variant(false), new Variant(true) },

                    new int[1]).toDispatch();

            // 作为html格式保存到临时文件

            Dispatch.invoke(doc, "SaveAs", Dispatch.Method, new Object[] {

                    toPath, new Variant(8) }, new int[1]);

            Variant f = new Variant(false);

            Dispatch.call(doc, "Close", f);

            axc.invoke("Quit", new Variant[] {});

            flag = true;

            return flag;

        } catch (Exception e) {

            e.printStackTrace();

            return flag;

        } finally {

            ComThread.Release();//关闭com的线程   真正kill进程
        }

    }




    /**
     *  读取本地html文件里的html代码
     * @return
     */
    public  String toHtmlString(String htmlFilePath) {
        // 获取HTML文件流
        Path filePath = Paths.get(htmlFilePath);
        StringBuffer htmlSb = new StringBuffer();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    new FileInputStream(filePath.toFile()), "GBK"));
            while (br.ready()) {
                htmlSb.append(br.readLine());
            }
            // HTML文件字符串
            String htmlStr = htmlSb.toString();
            //todo 处理html文本 添加预处理代码


            Document doc = Jsoup.parse(htmlStr);

            // 选择所有<p>标签
            for (Element p : doc.select("p")) {

                // 添加ID属性(例如,使用"id_" + 索引作为ID值)
                p.attr("id", "id_" + doc.select("p").indexOf(p));

                if (p.text().matches("(?i)第[\\u4e00-\\u9fa5]+章.*")) {
                    if(p.hasClass("MsoNormal")) {
                        p.addClass("level1");
                    }
                }
                // 检查文本是否包含“第*条”这样的模式(这里假设*是文本)
                if (p.text().matches("(?i)第[\\u4e00-\\u9fa5]+条.*")) { // 使用正则表达式匹配“第”后面跟着一个或多个数字,然后是“条”
                    // 添加class="level2"属性
                    p.addClass("level2");   // 如果元素已经有其他class,这将替换它。如果你想追加,请使用p.addClass("level2");
                }

            }
            htmlStr=doc.html();
            // 返回经过清洁的html文本
            try (BufferedWriter writer = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8)) {
                writer.write(htmlStr);
            }
            br.close();
            // 删除临时文件
            //file.delete();
            return htmlStr;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }


    /**
     * 清除文件中的table
     *
     * @param content
     *            公告内容
     * @return 字符串结果集
     */
    public static String removeContentStyle(String content) {
        String regEx = "<table(.*?)</table>";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(content);
        if (m.find()) {
            content = m.replaceAll("");
        }
//            String regEx2 = " style=\"([\\s\\S]*?)\"";
//            Pattern p2 = Pattern.compile(regEx2);
//            Matcher m2 = p2.matcher(content);
//            if (m2.find()) {
//                    content = m2.replaceAll("");
//            }
//            String regEx3 = " border=\"(.*?)\"";
//            Pattern p3 = Pattern.compile(regEx3);
//            Matcher m3 = p3.matcher(content);
//            if (m3.find()) {
//                    content = m3.replaceAll(" border=\"1\" ");
//            }
//
//            String regEx4 = " class=.*?\\>";
//            Pattern p4 = Pattern.compile(regEx4);
//            Matcher m4 = p4.matcher(content);
//            if (m4.find()) {
//                    content = m4.replaceAll("\\>");
//            }
//            String regEx5 = "\\<!--(.*?)--\\>";
//            Pattern p5 = Pattern.compile(regEx5);
//            Matcher m5 = p5.matcher(content);
//            if (m5.find()) {
//                    content = m5.replaceAll("");
//            }
//            String regEx6 = "\\<o:p(.*?)/o:p\\>";
//            Pattern p6 = Pattern.compile(regEx6);
//            Matcher m6 = p6.matcher(content);
//            if (m6.find()) {
//                    content = m6.replaceAll("");
//            }
//            String regEx7 = "\\<!(.*?)\\>";
//            Pattern p7 = Pattern.compile(regEx7);
//            Matcher m7 = p7.matcher(content);
//            if (m7.find()) {
//                    content = m7.replaceAll("");
//            }
//            String regEx8 = "\\<font(.*?)\\>";
//            Pattern p8 = Pattern.compile(regEx8);
//            Matcher m8 = p8.matcher(content);
//            if (m8.find()) {
//                    content = m8.replaceAll("");
//            }
        return content;
    }
二、 解析完的html数据或者是纯文本数据导入es

目前es和kibana我选择最新版的

PUT /word_index
{
    
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "analyzer": {
        "ik_max_word":{
          "type": "ik_max_word"
        },
        "ik_smart":{
          "type":"ik_smart"
        }
      }
    }
  },
   "mappings": {
  
       "properties": {
        "content":{
          "type": "text",
          "analyzer": "ik_smart"
        }
       }
  
   }
  
}

先来建一个索引(相当于数据库的表 用来存导入的word文本数据 后续搜索关键字用)

ik分词器用ik_smart智能 的就够了

然后测试一下搜索和高亮

GET /word_index/_search
{
  
  "query": {
    
    "match": {
      "content": "我的关键字"
    }
  },
  "highlight": {
    
    "fields": {
      "content":{
        "fragment_size": 100,
        "number_of_fragments": 5 ,
         "fragmenter": "simple"
      }
    },
    "pre_tags": ["<span style='color:#F00'>"],
    "post_tags": ["</span>"]
  }
}

一般来我喜欢返回前端高亮文本的样式是红色

解释一下这些参数

fragment_size

fragment_size 参数定义了在每个高亮片段中返回的字符数。这个值是一个近似值,因为Elasticsearch会尝试在单词边界上分割文本,而不是简单地按照字符数来截断文本。

number_of_fragments

number_of_fragments 参数定义了每个字段应返回的高亮片段的数量。默认情况下,Elasticsearch只返回一个片段,但你可以通过增加这个值来返回更多的片段。

fragmenter类型

1. simple

  • 行为:简单地按照fragment_size参数指定的字符数将文本分割成片段。
  • 示例:如果你设置fragment_size为100,则每个片段大约包含100个字符。

2. span

  • 行为:尝试在单词边界上分割文本,同时尽量遵守fragment_size参数。如果单词太大,它可能会超过fragment_size
  • 优点:通常会产生更有意义的片段,因为它们在单词边界上分割。

3. sentence

  • 行为:尝试在句子边界上分割文本。如果句子太短,可能会返回少于fragment_size的片段。
  • 适用场景:当你想在句子级别进行高亮时,这个片段器特别有用。

测试完成之后转成java代码 需要一些步骤

java进行http请求可能需要证书 上面网盘内下载并操作

        implementation 'co.elastic.clients:elasticsearch-java:8.13.0'
        implementation 'org.elasticsearch.client:elasticsearch-rest-client:8.13.0'
        implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

依赖是我用的gradle的 自行gpt转maven

   
    public  void main(String[] args) throws Exception {
        queryByContent("关键字")
        initEsConnection();
        transport.close();
    }
public static void initEsConnection() throws Exception {
        // 获取客户端对象
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        // 注意这里改成自己的账号密码
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));//es的账号密码
        //Path caCertificatePath = Paths.get("D:\\devlop\\jdk17\\lib\\security\\http_ca.crt"); 这个证书先放到本地,部署的时候线上放一个
      Path caCertificatePath = Paths.get("线上路径证书地址");//例如 /home/code/item/cart/http_cart
        CertificateFactory factory = CertificateFactory.getInstance("X.509");
        Certificate trustedCa;
        try (InputStream is = Files.newInputStream(caCertificatePath)) {
            trustedCa = factory.generateCertificate(is);
        }
        KeyStore trustStore = KeyStore.getInstance("pkcs12");
        trustStore.load(null, null);
        trustStore.setCertificateEntry("ca", trustedCa);
        SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(trustStore, null);
        final SSLContext sslContext = sslContextBuilder.build();
        // 主机名改成自己的
        RestClientBuilder builder = RestClient.builder(
                        new HttpHost(hostAddress, hostPort, "http"))
                .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                    @Override
                    public HttpAsyncClientBuilder customizeHttpClient(
                            HttpAsyncClientBuilder httpClientBuilder) {
                        return httpClientBuilder.setSSLContext(sslContext)
                                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                                .setDefaultCredentialsProvider(credentialsProvider);
                    }
                });
        RestClient restClient = builder.build();
        transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        // 将新创建的ElasticsearchClient实例赋值给类级别的静态变量client
        client = new ElasticsearchClient(transport);
        // 异步
//        ElasticsearchAsyncClient asyncClient = new ElasticsearchAsyncClient(transport);
        //查询索引
        GetIndexRequest getIndexRequest = new GetIndexRequest.Builder().index("dev_test_index").build();
        final GetIndexResponse getIndexResponse = client.indices().get(getIndexRequest);
        System.out.println( "索引查询成功: :" + getIndexResponse.result());
//        IndexRequest.Builder<Object> index = new IndexRequest.Builder<>().index("my_index");


    }
     //新增文档根据的文本内容
    public static  String createDoc(String htmlString)throws Exception{

        Map<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("content",htmlString);
        String id = SpringContextUtils.getSnowflakeGenerator().next().toString();
        IndexRequest indexRequest = new IndexRequest.Builder<>()
                .index("word_index")
                .id(id)
                .document(jsonMap)
                .build();
        final IndexResponse index = client.index(indexRequest);
        //关闭连接
        transport.close();
        return id;
    }
    //根据id查看文档
    public static  Object queryDocById(String id)throws Exception{
        SearchResponse<Object> response = client.search(s -> s
                        .index("word_index")
                        .query(q -> q.term(t -> t.field("_id").value(id))),
                Object.class);
                List<Hit<Object>> hits = response.hits().hits();
//        解析数据
        Object source=null;
        for (Hit<Object> hit : hits) {
           source = hit.source();
            System.out.println("查询结果为:"+source);
        }
        return source;
    }
    public  void deleteDoc(Long id)throws Exception{
        // 删除文档
        DeleteRequest deleteRequest = new DeleteRequest.Builder().index("word_index").id(id.toString()).build();
        DeleteResponse deleteResponse = client.delete(deleteRequest);
        System.out.println("删除操作"+deleteResponse);
        //关闭连接
        transport.close();
    }


//根据搜索的关键字查询文档并返回一些相关数据  伪代码,可以自己根据业务修改
 public static PageData queryByContent(int offset,int pageSize,String value,List<String> dictIds){


        SearchRequest request = SearchRequest.of(t->t
                .index("word_index").size(10000)
                .query(nq->nq.match(mtq->mtq
                        .field("content")
                        .query(value).analyzer("ik_smart")))
                .highlight(h->h.fields("content",f->f .preTags("<span style='color:#F00'>")
                        .postTags("</span>")))
        );
        SearchResponse searchResponse = null;

        try {
            PageData<Object> page = new PageData<>();
            ArrayList<Object> list = new ArrayList<>();
            searchResponse = client.search(request, JSONObject.class);
            List<Hit<Object>> hits = searchResponse.hits().hits();
            int total=0;
            for (Hit<Object> hit : hits) {
                HashMap<String, Object> map = new HashMap<>();
                map.put("id",hit.id());
           
            }
       
            page.setTotal(searchResponse.hits().total().value());
            return page;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
三、java的word转html另一种方式不需要引入jacob(jacob这个jar包只适用于windows插件,部署linux的时候不管用)
        // Apache POI 依赖项
        implementation 'org.apache.poi:poi:3.14'
        implementation 'org.apache.poi:poi-scratchpad:3.14'
        implementation 'org.apache.poi:poi-ooxml:3.14'
        implementation 'org.apache.poi:ooxml-schemas:1.3'
           // xdocreport 依赖项
        implementation group: 'fr.opensagres.xdocreport', name: 'xdocreport', version: '1.0.6'
        implementation 'org.apache.pdfbox:pdfbox:2.0.29'  //解析pdf
        implementation group: 'org.odftoolkit', name: 'odfdom-java', version: '0.8.7'
        // 添加jsoup的依赖
        implementation 'org.jsoup:jsoup:1.17.2'        
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>3.14</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-scratchpad</artifactId>
	<version>3.14</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>3.14</version>
</dependency>

<dependency>
	<groupId>fr.opensagres.xdocreport</groupId>
	<artifactId>xdocreport</artifactId>
	<version>1.0.6</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml-schemas</artifactId>
	<version>3.14</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>ooxml-schemas</artifactId>
	<version>1.3</version>
</dependency>

<dependency>
	<groupId>org.jsoup</groupId>
	<artifactId>jsoup</artifactId>
	<version>1.11.3</version>
</dependency>

两种格式的依赖

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.xwpf.converter.core.BasicURIResolver;
import org.apache.poi.xwpf.converter.core.FileImageExtractor;
import org.apache.poi.xwpf.converter.core.IXWPFConverter;
import org.apache.poi.xwpf.converter.xhtml.XHTMLConverter;
import org.apache.poi.xwpf.converter.xhtml.XHTMLOptions;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.jky.common.utils.SpringContextUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; 
@Service
public class ExtractDoc {
    @Value("${file.sysPath}")
    private String sysPath;
    @Value("${file.uploadPath}")
    private String uploadPath;
    public static void main(String[] args) {

        String fileDir = "C:\\Users\\Administrator\\Desktop\\解析docx数据\\";
       String htmlPath = "C:\\Users\\Administrator\\Desktop\\解析docx数据\\html\\";
       String filename="xxxdocx数据文件";

        docToHtml(fileDir,filename,htmlPath);


    }
public static String docToHtml(String fileDir, String filename,String htmlPath) {
       //把docx文件转成html存放到指定路径
        try {
            XWPFDocument document = new XWPFDocument(new FileInputStream(fileDir+filename+".docx"));
            XHTMLOptions options = XHTMLOptions.create();
            // 存放图片的文件夹
            options.setExtractor(new FileImageExtractor(new File(htmlPath+"images")));
            // html中图片的路径
            options.URIResolver(new BasicURIResolver(sysPath+"images"));
            IXWPFConverter<XHTMLOptions> xhtmlConverter = XHTMLConverter.getInstance();
            xhtmlConverter.convert(document, new FileOutputStream(htmlPath+filename+".html"), options);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return htmlPath+filename+".html";
    }
    /**
     *  读取本地html文件里的html代码
     * @return
 */
//中间一坨是我的业务需求代码,需要给内容的样式加一个id,搜索之后可以定位到html的哪个p标签  (先给p标签加上id和等级,然后给纯文本加上id,返回纯文本存到es)
    public  String toHtmlString(String htmlFilePath) {
        // 获取HTML文件流
        Path filePath = Paths.get(htmlFilePath);
        StringBuffer htmlSb = new StringBuffer();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    new FileInputStream(filePath.toFile()), "UTF-8"));
            while (br.ready()) {
                htmlSb.append(br.readLine());
            }
            // HTML文件字符串
            String htmlStr = htmlSb.toString();
            //todo 处理html文本 添加预处理代码
            Document doc = Jsoup.parse(htmlStr);
            // 选择<head>元素,如果不存在则创建一个
            Element head = doc.head();
            if (head == null) {
                head = new Element("head");
                doc.appendChild(head); // 添加到文档的开头
            }

            // 创建一个<meta charset="utf-8">元素
            Element meta = new Element("meta")
                    .attr("charset", "utf-8")
                    .attr("http-equiv", "Content-Type")
                    .attr("content", "text/html; charset=utf-8"); // 完整版本,包括http-equiv和content属性

            // 将<meta>元素添加到<head>元素中
            head.appendChild(meta);
            // 选择所有<p>标签
            for (Element p : doc.select("p")) {
//                // 添加ID属性(例如,使用"id_" + 索引作为ID值)
                p.attr("id",  doc.select("p").indexOf(p)+"");

                if (p.text().matches("(?i)第[\\u4e00-\\u9fa5]+章.*")) {
                    if(p.hasClass("MsoNormal")) {
                        p.addClass("level1");
                    }
                }
                // 检查文本是否包含“第*条”这样的模式(这里假设*是文本)
                if (p.text().matches("(?i)第[\\u4e00-\\u9fa5]+节.*")) { // 使用正则表达式匹配“第”后面跟着一个或多个数字,然后是“节”
                    // 添加class="level2"属性
                    p.addClass("level2");   // 如果元素已经有其他class,这将替换它。如果你想追加,请使用p.addClass("level2");
                }
                // 检查文本是否包含“第*条”这样的模式(这里假设*是文本)
                if (p.text().matches("(?i)第[\\u4e00-\\u9fa5]+条.*")) { // 使用正则表达式匹配“第”后面跟着一个或多个数字,然后是“条”
                    // 添加class="level3"属性
                    p.addClass("level3");
                }

            }
            htmlStr=doc.html();
            // 返回经过清洁的html文本
            try (BufferedWriter writer = Files.newBufferedWriter(filePath, StandardCharsets.UTF_8)) {
                writer.write(htmlStr);
            }
            for (Element p : doc.select("p")) {
                String id = "" + doc.select("p").indexOf(p); // 这里的 index 可能会因为动态文档而不准确

                TextNode idTextNode = new TextNode("<id=" + id + "> "); // 第二个参数是基准 URL,这里不需要
                TextNode contentTextNode = new TextNode(p.text()); // 第二个参数是基准 URL,这里不需要
                // 将 ID 文本节点和原始文本节点添加到 <p> 标签中
                p.appendChild(idTextNode);
              //  p.appendChild(contentTextNode);
            }
           String content=doc.body().text();

            br.close();
            // 删除临时文件
            //file.delete();
            return content;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }
  }

前端就可以根据p标签的格式和等级进行一个目录树以及章节锁定的业务
当然这个word文章格式规则是后端来制定的

ok到此 java的解析工作和存入es以及用es查询工作就到这里了

希望可以帮助你。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值