java导出word套打

news2024/11/18 5:53:30

 这篇文档手把手教你完成导出word套打,有这个demo,其他word套打导出都通用。

 1、主要依赖

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.0</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>5.3.0</version>
        </dependency>

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

        <dependency>
            <groupId>repository.org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
        <dependency>
            <groupId>repository.org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

 2、模板excel见附件。

3、以下为导出代码逻辑:

package com.example.demo.excel;

import cn.hutool.poi.word.PicType;
import lombok.Data;

/**
 * 图片对象
 * @author xiajun
 */
@Data
public class WordImage {
    /**
     * 图片宽度
     */
    private int width = 100;

    /**
     * 图片高度
     */
    private int height = 100;

    /**
     * 图片地址
     * resource资源相对路径
     */
    private String path;

    /**
     * 字节流
     */
    private byte[] source;

    /**
     * 图片类型
     * 默认PNG
     *
     */
    private PicType imageType = PicType.PNG;


}

package com.example.demo.excel;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.word.Word07Writer;
import cn.hutool.poi.word.WordUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * word导出
 * @author xiajun
 */
@Slf4j
@RestController
@RequestMapping("mvc/word")
public class WordExport {
    @GetMapping(path = "/export")
    public void export(HttpServletResponse response) throws IOException {

        //1、============================模拟数据===============================
        Map<String, Object> params = new HashMap<>();
        params.put("${name}", "张三");
        params.put("${sex}", "男");
        params.put("${birth}", "1999.11");
        params.put("${age}", "25");
        params.put("${nation}", "汉族");
        params.put("${npn}", "四川成都");
        params.put("${bpn}", "四川成都");
        params.put("${pt}", "2017.02");
        params.put("${workTime}", "2016.03");
        params.put("${hn}", "健康");
        params.put("${beFawts}", "打篮球");
        params.put("${proTec}", "高级工程师");
        params.put("${degree}", "大学本科");
        params.put("${education}", "文学学士");
        params.put("${school}", "北京大学文院");
        params.put("${major}", "语言学");
        params.put("${dutyDegree}", "研究生毕业");
        params.put("${dutyEducation}", "其他");
        params.put("${dutySchool}", "四川大学土木学院");
        params.put("${dutyMajor}", "工程造价");
        params.put("${jobName}", "党委书记,组织部长,宣传部部长");

        //图片设置
        WordImage wordImage = new WordImage();
        wordImage.setHeight(150);
        wordImage.setWidth(100);
        byte[] imageBytes = FileUtil.readBytes("templates/fm.png");
        wordImage.setSource(imageBytes);
        params.put("@{photo}", wordImage);

        //一段长文本拆分,按照分行进行展示,例如简历这种
        String resume = "2017.08--2018.08  北京大学光华管理学院工商管理就读研究生\r2023.05--2023.09  简历测试职务\r2023.05--2023.09  外部人员挂职职务\r2023.09--         图书档案资料人员";
        params.put("${resume}", resume);

        //2、============================获取word模板===============================
        Word07Writer writer = WordUtil.getWriter(FileUtil.file("templates/mb.docx"));
        XWPFDocument document = writer.getDoc();//获取模板文档

        //3、============================获取模板文本的段落===============================
        Iterator<XWPFParagraph> paragraphStrings = document.getParagraphsIterator();
        while (paragraphStrings.hasNext()) {
            XWPFParagraph paragraph = paragraphStrings.next();
            log.info("文本段落文字:{}", paragraph.getText());
        }

        //4、============================获取模板表格的段落===============================
        Iterator<XWPFTable> tableIterator = document.getTablesIterator();
        while (tableIterator.hasNext()) {
            XWPFTable table = tableIterator.next();
            Iterator<XWPFTableRow> rows = table.getRows().listIterator();
            while (rows.hasNext()) {
                XWPFTableRow row = rows.next();
                Iterator<XWPFTableCell> cells = row.getTableCells().listIterator();
                while (cells.hasNext()) {
                    XWPFTableCell cell = cells.next();
                    List<XWPFParagraph> paragraphCells = cell.getParagraphs();
                    //只去表格中的一个段落
                    XWPFParagraph paragraph = CollectionUtil.isNotEmpty(paragraphCells) ? paragraphCells.get(0) : null;
                    if(paragraph != null){
                        String paragraphString = paragraph.getText();//段落中的文字
                        //占位符替换
                        TemplateProcessor.matchTemplate(paragraphString, params, paragraph);
                    }
                }
            }
        }

        //5、======================输出文件====================================
        // 设置content—type
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset:utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=data.docx");

        // 设置标题
        //Content-disposition是MIME协议的扩展,MIME协议指示MIME用户代理如何显示附加的文件。
        ServletOutputStream outputStream = response.getOutputStream();

        //将Writer刷新到OutPut
        writer.flush(outputStream, true);
        outputStream.close();
    }
}
package com.example.demo.excel;

import cn.hutool.poi.word.PicType;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;

/**
 * word模板处理
 * @author xiajun
 */
@Slf4j
public class TemplateProcessor {


    final static Pattern patternString = Pattern.compile("\\$\\{[^{}]+}");//匹配文字
    final static Pattern patternTable = Pattern.compile("#\\{[^{}]+}");//匹配表格
    final static Pattern patternImg = Pattern.compile("@\\{[^{}]+}");//匹配照片

    /**
     * 模板占位符匹配
     *
     * @param template
     * @param variables
     * @return
     */
    public static void matchTemplate(String template, Map<String, Object> variables, XWPFParagraph paragraph) {
        //文字占位符替换
        Matcher matcher = patternString.matcher(template);
        while (matcher.find()) {
            String variable = matcher.group();//占位符
            dealXWPFRun(paragraph,variables,variable);//文档中占位符去除并设置套打文字
        }

        //图片占位符替换
        Matcher matcherImg = patternImg.matcher(template);
        while (matcherImg.find()) {
            String variable = matcherImg.group();//占位符
            dealXWPFRun(paragraph,variables,variable);//文档中占位符去除并设置套打图片

        }
    }

    /**
     * 获取文档占位符run,并且进行替换
     * @param paragraph
     * @param variables
     * @param variable
     */
    public static void dealXWPFRun(XWPFParagraph paragraph, Map<String, Object> variables, String variable){
        Object object = variables.get(variable);//需要替换的对象

        if (paragraph != null) {
            //获取占位符第一个
            String beginTag = variable.substring(0, 1);
            Iterator<XWPFRun> runs = paragraph.getRuns().listIterator();
            boolean flag = false;//是否开始拼接

            StringBuffer placeholder = new StringBuffer();
            List<List<XWPFRun>> allRuns = new ArrayList<>();//所有占位符涉及到的run
            List<XWPFRun> currentRuns = new ArrayList<>();//当前占位符涉及到的run
            while (runs.hasNext()) {
                XWPFRun run = runs.next();
                //开始符号处理
                if (run.text().contains(beginTag)) {
                    flag = true;
                }
                if (flag) {
                    String runString = run.text();
                    placeholder.append(runString);
                    currentRuns.add(run);
                }
                if (variable.equals(placeholder.toString())) {
                    allRuns.add(currentRuns);
                    currentRuns = new ArrayList<>();//重置list
                    flag = false;
                }
            }

            //将占位符涉及到的每个run list的第一个元素进行赋值,其他设置为空
            Iterator<List<XWPFRun>> iterator = allRuns.listIterator();
            while (iterator.hasNext()) {
                List<XWPFRun> runList = iterator.next();
                for (int i = 0; i < runList.size(); i++) {
                    if (i == 0) {
                        //文字处理
                        if(object instanceof String){
                            setRunText(runList.get(i),(String)object);
                        }
                        //图片处理
                        if(object instanceof WordImage){
                            setRunImg(runList.get(i),(WordImage)object);
                        }
                    } else {
                        runList.get(i).setText("", 0);
                    }
                }
            }
        }
    }

    /**
     * 设置Run中的图片
     * @param run
     * @param wordImage
     */
    private static void setRunImg(XWPFRun run, WordImage wordImage){
        PicType picType = wordImage.getImageType();
        try {
            run.setText("", 0);
            //往文档加入图片
            String picId = run.getDocument().addPictureData(wordImage.getSource(), picType.getValue());
            //将图片放在指定run中
            insertImageInRun(run, picId, run.getDocument().getNextPicNameNumber(picType.getValue()),
                    wordImage.getWidth(), wordImage.getHeight());
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置Run中的文本
     *
     * @param run
     * @param text
     */
    private static void setRunText(XWPFRun run, String text) {
        if (text.contains("\r")) {
            String[] split = text.split("\r");
            List<String> contents = Arrays.asList(split);

            run.setText(contents.get(0), 0);
            XWPFParagraph paragraph =(XWPFParagraph)run.getParent();

            XWPFTableCell cell = (XWPFTableCell)paragraph.getBody();

            for (int i = 1; i < contents.size(); i++) {

                //替换的肯定有段落获取第一个段落
                XWPFParagraph newParagraph = cell.addParagraph();
                newParagraph.getCTP().setPPr(paragraph.getCTP().getPPr());
                XWPFRun newRun = newParagraph.createRun();

                newRun.getCTR().setRPr(run.getCTR().getRPr());
                String content = contents.get(i);
                newRun.setText(content);
            }
        } else {
            run.setText(text,0);
        }
    }

    /**
     * run中插入图片
     *
     * @param run
     * @param picId
     * @param id
     * @param width
     * @param height
     */
    private static void insertImageInRun(XWPFRun run, String picId, int id, int width, int height) {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        CTInline inline = run.getCTR().addNewDrawing().addNewInline();

        String picXml = "" +
                "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
                "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "         <pic:nvPicPr>" +
                "            <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" +
                "            <pic:cNvPicPr/>" +
                "         </pic:nvPicPr>" +
                "         <pic:blipFill>" +
                "            <a:blip r:embed=\"" + picId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
                "            <a:stretch>" +
                "               <a:fillRect/>" +
                "            </a:stretch>" +
                "         </pic:blipFill>" +
                "         <pic:spPr>" +
                "            <a:xfrm>" +
                "               <a:off x=\"0\" y=\"0\"/>" +
                "               <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" +
                "            </a:xfrm>" +
                "            <a:prstGeom prst=\"rect\">" +
                "               <a:avLst/>" +
                "            </a:prstGeom>" +
                "         </pic:spPr>" +
                "      </pic:pic>" +
                "   </a:graphicData>" +
                "</a:graphic>";

        XmlToken xmlToken = null;
        try {
            xmlToken = XmlToken.Factory.parse(picXml);
        } catch (XmlException xe) {
//            xe.printStackTrace();
            throw new RuntimeException("图片XML解析失败");
        }
        inline.set(xmlToken);
        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("Picture " + id);
        docPr.setDescr("Generated");
    }
}

4、导出效果:

注意事项:如果你调整了代码设置数据,但是没生效,把target目录下的class文件删除重启哈就生效了。

============================好用记得点个赞哟!!!==========================

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

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

相关文章

为什么要做FP独立站?FP独立站有哪些优势?

近年来&#xff0c;跨境电商的商家们面临越来越大的平台政策压力&#xff0c;商家们纷纷把眼光聚焦到独立站上&#xff0c;眼下独立站已经成为出海卖家的标配。 特别是想做FP商品的卖家&#xff0c;相对于亚马逊平台&#xff0c;独立站才是你们的最终出路... 那么&#xff0c;问…

低代码平台可以开发哪些软件系统

在当今数字化时代&#xff0c;企业管理系统的开发已成为各行各业中不可或缺的一环。然而&#xff0c;传统的软件开发过程往往复杂且耗时&#xff0c;难以满足快速变化的市场需求。低代码平台的出现为企业管理系统的开发带来了革命性的变革。本文将探讨低代码平台在企业管理系统…

菱形以及各种组合图形讲解(*#@¥$)

引言&#xff1a; ***形对于新手了解循环以及嵌套循环帮助是非常大的。&#xff08;以下的题各题之间有关联&#xff09; 我们最终目的&#xff0c;就是会编程写菱形&#xff1b;看下面的图片 解题思路&#xff1a;运用拆分法&#xff0c;我们将菱形分为4个部分&#xff0c;看…

MySQL一主一从读写分离

​ MySQL主从复制 一、主从复制概念 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从服务器中&#xff0c;然后在从服务器上对这些日志重新执行也叫重做&#xff0c;从而使得从数据库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进行赋值&#xff0c;从…

Halcon实例:提取图像的纹理特征

Halcon实例&#xff1a;提取图像的纹理特征 举例说明&#xff0c;输入的是一幅灰度图像&#xff0c;分别选取其中两个矩形区域的灰度图像&#xff0c;分析其灰度变化。首先选取灰度变化较为明显的矩形1&#xff0c;然后选取灰度变化比较平滑的矩形2&#xff0c;生成灰度共生矩…

AD软件与其他EDA软件工程的问题汇总

1:如何在AD中使用eagle工程 在ad中打不开原理图&#xff0c;要使用导入功能,转化为ad的文件后&#xff0c;就可以打开了 2:打开旧版本的Protel文件 有时候新版本的AD打不开以前Protel的PCB文件&#xff0c;可以在DXP菜单下的Extension下进行配置&#xff08;Configure&…

高效降压控制器FP7132XR:为高亮度LED提供稳定可靠的电源

目录 一. FP7132概述 二. 驱动电路&#xff1a;FP7132 三. FP7132应用 高亮度LED作为新一代照明技术的代表&#xff0c;已经广泛应用于各种领域。然而&#xff0c;高亮度LED的工作电压较低&#xff0c;需要一个高效降压控制器来为其提供稳定可靠的电源。在众多降压控制器…

【AI大模型应用开发】1.0 Prompt Engineering(提示词工程)- 典型构成、原则与技巧,代码中加入Prompt

从这篇文章开始&#xff0c;我们就正式开始学习AI大模型应用开发的相关知识了。首先是提示词工程&#xff08;Prompt Engineering&#xff09;。 文章目录 0. 什么是提示词&#xff08;Prompt&#xff09;1. 为什么Prompt会起作用 - 大模型工作原理2. Prompt的典型构成、原则与…

ubuntu20.04 deepstream 6.3安装

1.基础环境gstreamer sudo apt install \ libssl-dev \ libgstreamer1.0-0 \ gstreamer1.0-tools \ gstreamer1.0-plugins-good \ gstreamer1.0-plugins-bad \ gstreamer1.0-plugins-ugly \ gstreamer1.0-libav \ libgstreamer-plugins-base1.0-dev \ libgstrtspserver-1.0-0 …

微信小程序开发学习笔记《8》tabBar

微信小程序开发学习笔记《8》tabBar 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。tabBar官方文档 tabBar这一节还是相当重要的。 一、什么是tabBar tabBar是移动端应用常见的页面效果&#xff0c;用于实现多页面的快速切换。小…

第十四章JSON

第十四章JSON 1.什么是JSON2.JSON的定义和访问3.JSON在JavaScript中两种常用的转换方式4.JavaBean和JSON的相互转换5.List集合和JSON的相互转换6.map集合和JSON的相互转换 1.什么是JSON 2.JSON的定义和访问 JSON的定义 JSON的类型是一个Object类型 JSON的访问 我们要…

kafka下载安装部署

Apache kafka 是一个分布式的基于push-subscribe的消息系统&#xff0c;它具备快速、可扩展、可持久化的特点。它现在是Apache旗下的一个开源系统&#xff0c;作为hadoop生态系统的一部分&#xff0c;被各种商业公司广泛应用。它的最大的特性就是可以实时的处理大量数据以满足各…

详解如何撰写一个基础的技术交底书

大家好,我是英子老师。作为一名知识产权专家,深耕于专利行业十余年,具有丰富的专利工作经验:曾在大型专利代理机构从事专利代理工作、专利质检工作(抽查代理机构的专利代理人的撰写质量并评分);之后在知名上市企业、行业龙头企业担任高级专利工程师的职位,主要工作内容…

使用Flash_Download_Tool下载PlatformIO生成的bin程序到ESP32

使用Flash_Download_Tool下载PlatformIO生成的bin程序到ESP32 来源 当我们没有PlatformIO环境时&#xff0c;还要下载PlatformIO生成的程序时&#xff0c;可以使用Flash_Download_Tool工具下载。 说明 使用PlatformIO时&#xff0c;用cmd终端命令下载程序pio run -v -t upl…

MySQL面试题 | 01.精选MySQL面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例3-1 CSS3过渡

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>CSS3 过渡</title> <style> /*显示*/ .box {width: 100px;height: 100px;background-color: #eee;/*透明度*/opacity: 1;/*过渡*/transition: 3s; } /…

李沐之经典卷积神经网络

目录 1. LeNet 2. 代码实现 1. LeNet 输入是32*32图片&#xff0c;放到一个5*5的卷积层里面&#xff0c;卷积层的输出通道数是6&#xff0c;高宽都是28&#xff08;32-5128&#xff09;。再经过2*2的池化层&#xff0c;把28*28变成14*14&#xff08;28-22&#xff09;/214&am…

[BJDCTF2020]ZJCTF,不过如此

题目源码&#xff1a; <?phperror_reporting(0); $text $_GET["text"]; $file $_GET["file"]; if(isset($text)&&(file_get_contents($text,r)"I have a dream")){echo "<br><h1>".file_get_contents($tex…

Qt优秀开源项目之二十一:遇见QSkinny,一个轻量级Qt UI库

目录 一.QSkinny简介 二.工作原理 三.编译 一.QSkinny简介 QSkinny库基于Qt Graphic View和Qt/Quick中少量的核心类。它提供了一组轻量级控件&#xff0c;可以在C或QML中使用这些控件。QSkinny默认是启用硬件加速的&#xff0c;非常适合嵌入式设备&#xff0c;目前已经应用于…

react hooks 高德地图的应用

一、准备 1.登录控制台 登录 高德开放平台控制台&#xff0c;如果没有开发者账号&#xff0c;请 注册开发者。 2.创建 key 进入应用管理&#xff0c;创建新应用&#xff0c;新应用中添加 key&#xff0c;服务平台选择 Web端(JS API)。 3.获取 key 和密钥 创建成功后&#x…