Java的POI-word模板生成目录自动更新--完美解决--优化填坑

目录问题:

解决word模板目录在第一次打开不更新就不显示目录问题的原因:之前是通过动态替换域代码toc的形式,生成了一段域代码放置在Word的目录行,打开的时候无法直接触发渲染和更新。

方案:通过插入-文档组件-域组件-目录和索引,将当前的模板的目录直接生成到文档的目录中,在数据替换的时候,由于目录用的也是正文的内容,所以直接就替换掉了。

上述方案解决了需要手动更新才能显示,否则空白的问题。但是也存在缺点:只能更新目录的内容,目录的页码无法正确更新显示,是当时模板的页码。

------------------------------------------分界线-------------------------------------------------------------------------

项目时间允许之际,又做了方案调研,用以下方案,完美解决:

1、spire.doc

有收费版本和免费版本,免费的版本只能读取500行的内容,并生成目录,所以不全,不用;

收费版本的生成word后,会在文档第一行显示试用提示,将该行用poi删除即可;

先用poi-tl的模板生成word,然后用spire.doc打开,并更新域,再保存到原文件路径即可,最后将第一行删掉:


public class Demo4 {
    public static void main (String[] args) throws IOException {
        //加载已设置大纲级别的测试文档
        long start = System.currentTimeMillis();
        Document doc = new Document("D:\\project\\util\\src\\main\\resources\\poi\\report.docx");

        doc.updateTableOfContents();

        doc.saveToFile("目录2222311.docx", FileFormat.Docx_2010);
        restWord("目录2222311.docx");
        System.out.println((System.currentTimeMillis()-start)/1000);
    }
    private static void restWord(String docFilePath) {
        try (FileInputStream in = new FileInputStream(docFilePath)) {
            XWPFDocument doc = new XWPFDocument(OPCPackage.open(in));
            List<XWPFParagraph> paragraphs = doc.getParagraphs();
            if (paragraphs.size() < 1) return;
            XWPFParagraph firstParagraph = paragraphs.get(0);
            if (firstParagraph.getText().contains("Spire.Doc")) {
                doc.removeBodyElement(doc.getPosOfParagraph(firstParagraph));
            }
            OutputStream out = new FileOutputStream(docFilePath);
            doc.write(out);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、aspose.word

aspose.word只有收费版本,如果公司允许,可以买了再用,没有预算的,可以找一个破解jar包引入即可,如果找不到,可以私信我:

先用poi-tl的模板生成word,然后用aspose.word打开,并更新域,再保存到原文件路径即可:

public class Demo5 {
    public static void main (String[] args) throws Exception {
        //加载已设置大纲级别的测试文档
        long start = System.currentTimeMillis();
        Document doc = new Document("D:\\project\\util\\src\\main\\resources\\poi\\report.docx");
        doc.updateFields();
        doc.save("33333.pdf", SaveFormat.PDF);//这里执行操作

//        restWord("目录33331221.docx");

        System.out.println((System.currentTimeMillis()-start)/1000);
    }
    private static void restWord(String docFilePath) {
        try (FileInputStream in = new FileInputStream(docFilePath)) {
            XWPFDocument doc = new XWPFDocument(OPCPackage.open(in));
            List<XWPFParagraph> paragraphs = doc.getParagraphs();
            if (paragraphs.size() < 1) {
                return;
            }
            XWPFParagraph firstParagraph = paragraphs.get(0);
            XWPFParagraph lastParagraph = paragraphs.get(paragraphs.size() - 1);
            if (firstParagraph.getText().contains("Aspose.Words")) {
                doc.removeBodyElement(doc.getPosOfParagraph(firstParagraph));
                doc.removeBodyElement(doc.getPosOfParagraph(lastParagraph));
            }
            OutputStream out = new FileOutputStream(docFilePath);
            doc.write(out);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

总结,公司允许用破解版的,可以用aspose.word,效率高,生成PDF也给力;spire.doc是国内的产品,操作word耗时长,700+页的文档需要130s+的耗时,不能忍。

-------------------------------------------分界线----------------------------------------------------------------------

以上方案已经解决了目录精确问题,在大文件word生成的时候,还存在2个问题:

1、并发调用会导致线程卡住,卡在new Document()

2、大文件更新目录的时候特别慢,也会出现卡死现象

解决方案:

1、添加synchronized同步锁,每次只有一个生成;

2、判断文件大小超过nMB时,不调用精确方法,小于的时候才调用,避免不可用问题

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 50
    评论
使用 poi-tl 导出 Word 文档时,生成目录也可以使用 `TOCRenderPolicy` 策略。下面是一个简单的 Java 示例代码: ```java // 创建一个上下文对象 Map<String, Object> context = new HashMap<>(); // 往上下文中添加需要生成目录的内容 context.put("chapter1", "第一章"); context.put("chapter1Content", "第一章的内容..."); context.put("chapter2", "第二章"); context.put("chapter2Content", "第二章的内容..."); // 生成目录 TOCRenderData tocData = new TOCRenderData(); tocData.setTitle("目录"); // 设置目录标题 tocData.setStyle("TOC"); // 设置目录样式 tocData.setTocTitle("目录"); // 设置目录的标题 context.put("toc", tocData); // 将生成目录添加到上下文中 // 使用 poi-tl 导出模板 InputStream is = new FileInputStream("template.docx"); XWPFTemplate template = XWPFTemplate.compile(is).render(context); // 将生成的文档保存到文件 FileOutputStream out = new FileOutputStream("example.docx"); template.write(out); out.close(); template.close(); ``` 在这个示例中,我们先创建一个上下文对象,将需要生成目录的内容添加到上下文中,然后使用 `TOCRenderData` 对象生成目录,并将生成目录添加到上下文中。最后,使用 poi-tl 导出模板,并将生成的文档保存到文件中。 需要注意的是,使用 poi-tl 导出 Word 文档时,生成目录需要在模板中添加一个目录标记,如下所示: ``` ${toc} ``` poi-tl 会将这个标记替换为生成目录

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值