Java文件:XWPFDocument导出Word文档

news2024/12/26 21:46:32

文章目录

  • 一、前言
  • 二、基本的概念
  • 三、Maven依赖(JAR)
  • 四、Word模板
    • 1.正文段落
    • 2.正文表格
    • 3.页眉
    • 4.页脚
  • 五、XWPFDocument的使用
    • 5.4导出Word文档
      • 1.word模板
      • 2.PdfTest测试类
      • 3.ISystemFileService接口
      • 4.SystemFileServiceImpl实现类
      • 5.结果
  • 六、遇到问题
    • 5.1输出为word的时候换行符无效

一、前言

在Java项目开发过程中经常会遇到导出Word文档的业务场景。XWPFDocument是apache基金会提供的用户导出Word文档的工具类。

二、基本的概念

  • XWPFDocument:代表一个docx文档
  • XWPFParagraph:代表文档、表格、标题等各种的段落,由多个XWPFRun组成
  • XWPFRun:代表具有同样风格的一段文本
  • XWPFTable:代表一个表格
  • XWPFTableRow:代表表格的一行
  • XWPFTableCell:代表表格的一个单元格
  • XWPFChar:表示.docx文件中的图表
  • XWPFHyperlink:表示超链接
  • XWPFPicture:代表图片
  • XWPFComment :代表批注
  • XWPFFooter:代表页脚
  • XWPFHeader:代表页眉
  • XWPFStyles:样式(设置多级标题的时候用)

三、Maven依赖(JAR)

<!--  poi pdf文件/xml文件  -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.10-FINAL</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>

四、Word模板

1.正文段落

一个文档包含多个段落,一个段落包含多个Runs,一个Runs包含多个Run,Run是文档的最小单元

获取所有段落:List paragraphs = word.getParagraphs();

获取一个段落中的所有Runs:List xwpfRuns = xwpfParagraph.getRuns();

获取一个Runs中的一个Run:XWPFRun run = xwpfRuns.get(index);

XWPFRun–代表具有相同属性的一段文本

2.正文表格

一个文档包含多个表格,一个表格包含多行,一行包含多列(格),每一格的内容相当于一个完整的文档

获取所有表格:List xwpfTables = doc.getTables();

获取一个表格的行数:int rcount = xwpfTable.getNumberOfRows();

获取一个表格的第几行:XWPFTableRow row = table.getRow(i);

获取一个表格中的所有行:List xwpfTableRows = xwpfTable.getRows();

获取一行中的所有列:List xwpfTableCells = xwpfTableRow.getTableCells();

获取一格里的内容:List paragraphs = xwpfTableCell.getParagraphs();

之后和正文段落一样

注:

  1. 表格的一格相当于一个完整的docx文档,只是没有页眉和页脚。里面可以有表格,使用xwpfTableCell.getTables()获取,and so on
  2. 在poi文档中段落和表格是完全分开的,如果在两个段落中有一个表格,在poi中是没办法确定表格在段落中间的。(当然除非你本来知道了,这句是废话)。只有文档的格式固定,才能正确的得到文档的结构

3.页眉

一个文档可以有多个页眉,页眉里面可以包含段落和表格

获取文档的页眉:List headerList = doc.getHeaderList();

获取页眉里的所有段落:List paras = header.getParagraphs();

获取页眉里的所有表格:List tables = header.getTables();

4.页脚

页脚和页眉基本类似,可以获取表示页数的角标

五、XWPFDocument的使用

5.4导出Word文档

1.word模板

在resources目录下准备好word模板:xiaoshen.docx

在这里插入图片描述

2.PdfTest测试类

package com.shenxm.file.pdf.test;

import com.shenxm.file.pdf.service.impl.SystemFileBizImpl;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

/**
 * @Author: shenxm
 * @Description: pdf测试
 * @Version 1.0
 */
public class PdfTest {

    @Test
    public void  test1(){
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String format = simpleDateFormat.format(date);

        HashMap<String, Object> boMap = new HashMap<>();
        boMap.put("address","北京");
        boMap.put("name","法克\n蒙克丽丽\n娜娜");
        boMap.put("datetime","\n"+format);
        boMap.put("opinion","\n小沈\n"+format+"\n审批通过");
        boMap.put("book","春的林野");
        String docxTemplate = "xiaoshen.docx";//docx模板
        String pdfFileName = "xiaoshen.pdf";//输出的pdf
        SystemFileBizImpl systemFileBiz = new SystemFileBizImpl();
        systemFileBiz.exportPdf(boMap,docxTemplate,pdfFileName);
    }
}

3.ISystemFileService接口

package com.shenxm.file.pdf.service;
import com.shenxm.file.pdf.entity.DownloadFileBo;
import java.util.Map;

public interface ISystemFileService {
    DownloadFileBo exportPdf(Map<String,Object> map,String template,String fileName);
}

4.SystemFileServiceImpl实现类

package com.shenxm.file.pdf.service.impl;

import com.shenxm.file.pdf.entity.DownloadFileBo;
import com.shenxm.file.pdf.service.ISystemFileService;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @Author: shenxm
 * @Description: 文件处理 Service
 * @Version 1.0
 */
@Service
public class SystemFileServiceImpl implements ISystemFilService {

    @Override
    public DownloadFileBo exportPdf(Map<String, Object> map, String docxTemplateName, String pdfFileName) {
        //校验参数
        Assert.notEmpty(map, "数据源不可为空!");
        Assert.notNull(docxTemplateName,"docxTemplateName不能为空");
        Assert.notNull(pdfFileName,"pdfFileName不能为空");

        String pdfExportPath = "G:" + File.separator + "test1" + File.separator;
        //1.生成pdf文件对象
        File pdfFile = this.createFile(pdfExportPath, pdfFileName);

        String docxExportPath = "G:" + File.separator + "test1" + File.separator;//生成的word的路径
        String docxExportName ="xiaoshen1.docx";
        //使用当前线程的类加载器读取文件
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(docxTemplateName);
        if (in == null) {
            System.out.println("读取文件失败!");
        } else {
            try {
                //读取模板文档
                XWPFDocument document = new XWPFDocument(in);
                //替换段落中的${}
                this.replaceTextInParagragh(document, map);
                //替换表格中的${}
                this.replaceTextInTables(document, map);
                //TODO 替换其他的

                //将Docx文档写入文件
                File exportWord = new File(docxExportPath + docxExportName);
                FileOutputStream fileOutputStream = new FileOutputStream(exportWord);
                //输出文件
                document.write(fileOutputStream);
                fileOutputStream.flush();
                //TODO word转为pdf

                //关闭流
                fileOutputStream.close();
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /** 替换表格中的占位符 */
    private void replaceTextInTables(XWPFDocument document, Map<String, Object> dataMap) {
        //获取所有的表格
        List<XWPFTable> tables = document.getTables();
        //循环
        for (XWPFTable table : tables) {
            //获取每个表格的总行数
            int rcount = table.getNumberOfRows();
            for (int i = 0; i < rcount; i++) {
                //获取表格的第i行
                XWPFTableRow row = table.getRow(i);
                //获取一行的所有单元格
                List<XWPFTableCell> cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    //一个cell相当于一个document
                    //获取单元格内的文本
                    String cellTextString = cell.getText();
                    //替换文本:${} -> value
                    cellTextString = this.replaceText(cellTextString, dataMap);
                    //移除表格中的段落
                    while (cell.getParagraphs().size() > 0) {
                        cell.removeParagraph(0);
                    }
                    //处理换行,并设置单元格内容
                    this.setWrap(cellTextString,cell);
                }
            }
        }
    }


    /** 替换段落中的占位符 */
    private void replaceTextInParagragh(XWPFDocument document, Map<String, Object> dataMap) {
        //获取整个Word所有段落:包含页眉或页脚文本的段落
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        //循环
        for (XWPFParagraph paragragh : paragraphs) {
            //获取一段的所有本文
            List<XWPFRun> runs = paragragh.getRuns();
            //获取段落内容:paragragh.getText();
            //循环
            for (int i = 0; i < runs.size(); i++) {
                //XWPFRun--代表具有相同属性的一段文本
                XWPFRun xwpfRun = runs.get(i);
                //获取文本中的内容
                String paraString = xwpfRun.getText(xwpfRun.getTextPosition());
                if (paraString != null) {
                    //替换文字
                    paraString = this.replaceText(paraString, dataMap);
                    //设置替换后的段落
                    xwpfRun.setText(paraString, 0);
                }
            }
        }
    }

    /** 替换文字 */
    private String replaceText(String text, Map<String, Object> dataMap) {
        String paraString = text;
        //遍历map,将段落里面的${}替换成map里的value
        Iterator<Map.Entry<String, Object>> iterator = dataMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            String key = entry.getKey();
            String value =  entry.getValue().toString();
            //组装map里的key为${key}
            StringBuffer sb = new StringBuffer();
            String placeHolder = sb.append("${").append(key).append("}").toString();
            //替换:将"${as}dasdas" --> value+dasdas
            paraString = paraString.replace(placeHolder, value);
        }
        return paraString;
    }

    /** 单元格内设置换行 */
    private void setWrap(String cellTextString,XWPFTableCell cell){
        if (cellTextString != null &&cellTextString.trim().contains("\n")){
            //创建文本
            XWPFRun run = cell.addParagraph().createRun();
            String[] split = cellTextString.split("\n");
            run.setText(split[0],0);
            for (int i = 1; i < split.length; i++) {
                //添加换行符
                run.addBreak();
                //设置单元格内容
                run.setText(split[i]);
            }
        }else {
            //设置单元格内容
            cell.setText(cellTextString);
        }
    }

    /** 根据路径和文件名 创建文件对象*/
    private File createFile(String filePath,String fileName){
        //pdf目录对象
        File file = new File(filePath);
        if (!file.exists() || !file.isDirectory()) {
            file.mkdirs();
        }
        //pdf文件对象
        StringBuffer filePathBuffer = new StringBuffer();
        filePathBuffer.append(filePath).append(fileName);
        return new File(filePathBuffer.toString());
    }
}

5.结果

在这里插入图片描述

六、遇到问题

5.1输出为word的时候换行符无效

java换行符"\n"在word文档中不生效,使用"\r",“\r\n”,“(char)11”,“^p”,“br”,“<w:br>”,"w:p"等均无法实现单元格内换行的功能。

实现单元格内自动换行:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/165404.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NR5G基础概念扫盲

文章目录前言BWP未完待续前言 随着人工智能、万物互联时代的到来&#xff0c;人类社会进入到一个新的阶段。新兴的科技产业对信息社会基础设施提出了更高的要求&#xff0c;对低时延、大带宽、高流量的需求&#xff0c;催生了5G技术&#xff0c;并推动其蓬勃发展。通信&#x…

【深入浅出XML】包装纯粹信息的标记语言

XMLXML的定义和概述&#x1f3b6;XML的定义&#x1f3b6;XML的最好描述&#x1f3b6;HTML和XML的重要区别&#x1f3b6;XML的文档结构&#x1f3b6;其他一些标记XML和优势&#x1f3b6;XML的优势XML解析&#x1f3b6;DOM解析❔解析测试&#x1f91e;解析步骤&#x1f91e;案例测…

在Windows部署Java的Jar包

背景 使用 Java 编写了一些有用的工具&#xff0c;因为不方便部署到服务器上&#xff0c;所以需要把 Java 生成的 jar 包在本地 Windows 上部署。 查阅了几种部署方式&#xff0c;认为通过 winsw 进行部署最方便。 安装 winsw 进入 winsw 的下载页面&#xff0c;下载 sampl…

【ROS2 入门】ROS 2 参数服务器(parameters)概述

大家好&#xff0c;我是虎哥&#xff0c;从今天开始&#xff0c;我将花一段时间&#xff0c;开始将自己从ROS1切换到ROS2&#xff0c;在上一篇中&#xff0c;我们一起了解ROS 2中Topic&#xff0c; 这一篇&#xff0c;我们主要会围绕ROS中另外一个重要的概念“Parameters ”&am…

图的拓扑排序(AOV网络)

文章目录拓扑排序概念实现邻接表(队列)邻接矩阵(栈)总结源代码邻接表邻接矩阵拓扑排序 概念 拓扑排序是对有向无环图的顶点的一种排序. AOV网络 : 在有向图中, 用顶点表示活动或者任务, 弧表示活动或者任务间的优先关系, 则此有向图称为用顶点表示活动的网络(Activity On Ve…

小程序介绍和注册安装

小程序介绍和注册安装微信小程序介绍小程序特点其它平台小程序注册微信小程序开发帐号获取appidappid简介微信开发者工具安装创建一个小程序项目核心步骤微信开发者工具构成微信小程序介绍 简短定义&#xff1a;微信小程序是运行在微信APP中的一个程序。 常见小程序 行程码拼…

UDS诊断系列介绍11-3E服务

本文框架1. 系列介绍1.1 3E服务概述2. 3E服务请求与应答2.1 3E服务请求2.2 3E服务正响应2.3 3E服务否定响应3. Autosar系列文章快速链接1. 系列介绍 UDS&#xff08;Unified Diagnostic Services&#xff09;协议&#xff0c;即统一的诊断服务&#xff0c;是面向整车所有ECU的…

# 【笔记】大话设计模式21-23

【笔记】大话设计模式21-23 文章目录【笔记】大话设计模式21-23单例模式21.1 Example21.2 定义21.3 Show me the code一般单例代码(**懒汉模式**)静态初始化&#xff08;**饿汉模式**&#xff09;21.4 总结22 桥接模式22.1 Example22.2 定义22.3 Show me the code22.4 总结23 命…

Code for VeLO 1: Training Versatile Learned Optimizers by Scaling Up

Code for VeLO 1: Training Versatile Learned Optimizers by Scaling Up 这篇文章将介绍一下怎么用VeLO进行训练。 这篇文章基于https://colab.research.google.com/drive/1-ms12IypE-EdDSNjhFMdRdBbMnH94zpH#scrollToRQBACAPQZyB-&#xff0c;将介绍使用learned optimizer in…

入门力扣自学笔记230 C++ (题目编号:2293)

2293. 极大极小游戏 题目&#xff1a; 给你一个下标从 0 开始的整数数组 nums &#xff0c;其长度是 2 的幂。 对 nums 执行下述算法&#xff1a; 设 n 等于 nums 的长度&#xff0c;如果 n 1 &#xff0c;终止 算法过程。否则&#xff0c;创建 一个新的整数数组 newNums …

【Python百日进阶-数据分析】Day226 - plotly的仪表盘go.Indicator()

文章目录一、语法二、参数三、返回值四、实例4.1 Bullet Charts子弹图4.1.1 基本子弹图4.1.2 添加步骤和阈值4.1.3 自定义子弹4.1.4 多子弹4.2 径向仪表图4.2.1 基本仪表4.2.2 添加步骤、阈值和增量4.2.3 自定义仪表图4.3 组合仪表图4.3.1 组合仪表图4.3.2 单角量规图4.3.3 子弹…

Android 深入系统完全讲解(19)

技术的学习关键点 是什么&#xff1f;思路。 而我这里分享一个学习的经典路线&#xff0c;先厘清总框架&#xff0c;找到思路&#xff0c;然后再逐步击破。 这里关于音视频的就是&#xff1a; 总体分为几部分&#xff1a; 1 绘制 2 编解码格式 3 Android 平台的 FFmpeg 开源移…

Compressed Sensing——从零开始压缩感知

Problem 考虑一个线性方程组求解问题&#xff1a; Axb(1)A x b \tag{1}Axb(1) 其中&#xff0c;A∈RmnA \in\mathbb R^{m\times n}A∈Rmn&#xff0c;x∈Rn1x \in\mathbb R^{n\times 1}x∈Rn1&#xff0c;b∈Rm1b \in\mathbb R^{m\times 1}b∈Rm1且m≪nm \ll nm≪n 这是一个…

【C++11】—— lambda表达式

目录 一、lambda表达式的简介 二、lambda表达式的基本语法 三、lambda表达式的使用方法 四、lambda表达式的底层原理 一、lambda表达式的简介 lambda表达式就类似于仿函数&#xff0c;相比仿函数要更加的简洁&#xff0c;我们看一下下面的代码&#xff1a; //商品类 struct…

【项目实战】使用MybatisPlus乐观锁插件功能

一、背景 当要更新一条记录时&#xff0c;希望这条记录没有被别人更新&#xff0c;可以考虑使用MybatisPlus乐观锁插件功能来实现以上需求。 二、乐观锁介绍 2.1 乐观锁是什么&#xff1f; 乐观锁是一种乐观思想&#xff0c;即认为读多写少&#xff0c;遇到并发的可能性低&…

使用ASM框架创建ClassVisitor时遇到IllegalArgumentException的一种可能解决办法

背景 ASM是java语言中最为广泛使用的插装框架&#xff0c;其优点在于可以动态地在运行时改变java系统的行为&#xff0c;加入我们自己的逻辑。在软件测试领域应用广泛。但是其使用难度很高&#xff0c;一方面使用asm框架需要对java底层知识有较高的了解&#xff0c;另一方面网…

网页共享电脑屏幕与播放(带声音)

这次项目我们是写的一个课堂辅助软件的网页版&#xff0c;其中有一个功能感觉能作为我们项目的一个亮点&#xff0c;就是直播功能&#xff0c;在之前并没有写过这个东西。虽然现在这个功能还不知道怎么写&#xff0c;但是它的流程终归是利用视频流将本地的视频给共享出去&#…

Verilog:【8】基于FPGA实现SD NAND FLASH的SPI协议读写

碎碎念&#xff1a; 终于熬过了期末周&#xff0c;可以开始快乐的开发之旅了。 这一期作为一千粉后的首篇博客&#xff0c;由于之后项目会涉及到相关的部分&#xff0c;因此介绍的是使用FPGA实现SD NAND FLASH的读写操作&#xff0c;以雷龙科技提供的SD NAND FLASH样品为例&…

实证分析权重系数计算大全

在实际研究中&#xff0c;权重计算是一种常见的分析方法&#xff0c;需要结合数据的特征情况进行选择&#xff0c;比如数据之间的波动性是一种信息量&#xff0c;那么可考虑使用CRITIC权重法或信息量权重法&#xff1b;也或者专家打分数据&#xff0c;那么可使用AHP层次法或优序…

直观感受PromQL及其数据类型

由于PromQL内容较多&#xff0c;将内容分为三篇文章讲述&#xff1a; 一、直观感受PromQL及其数据类型 二、PromQL之选择器和运算符 三、PromQL之函数 想必都知道要使用Msql&#xff0c;必须会用SQL&#xff0c;同样要使用Prometheus 就要掌握PromQL&#xff08;Prometheus Que…