【java spring boot使用easypoi实现word文档占位符替换文字和图片、Excel图片贴到对应单元格】

最近接到一个任务,写两个导出工具:1.word文档导出,将数据和图片放入到word中,将多个word合并为一个导出。2.Excel文档导出,将二维码信息和图片按照模板放入到Excel中,按9个一页排版数据导出
这里word数据插入我选择使用占位符替换内容

word文档占位符文字和图片的替换

先看效果。如下图所示:
在这里插入图片描述

一、引入依赖

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

二、word导出工具

1.定义一个用来导出数据的实体类TicketProject

这里偷懒了,用@Builder方便造数据

import lombok.Builder;
import lombok.Data;

import java.io.Serializable;

@Builder
@Data
public class TicketProject implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 序列码 VvjiN63o9m
     */
    private String evaluationCode;
    /**
     * 二维码文件地址 https://www.hjhrcloud.com/e/
     */
    private String evaluationQrcode;
    /**
     * 项目名称
     */
    private String evaluationProjectName;
    /**
     * 票种角色(领导班子、外部董事、中层)
     */
    private String ticketName;
    /**
     * 描述 请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分
     */
    private String describe;

    /**
     * 简介(一页一码使用:欢迎参加2023年×××民主测评。。。)
     */
    private String information;
	/**
	 * 指导语
	 */
	private String guide;
	/**
	 * 注意事项
	 */
	private String attention;

    /**
     * 日期
     */
    private String createdDate;
}

2.word导出工具类 ExportUtils
import com.nnmzkj.common.utils.UUIDUtils;
import com.nnmzkj.evaluation.model.TicketProject;
import lombok.SneakyThrows;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.Document;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;

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

/**
 * 测评文件导出工具类
 */
public class ExportUtils {
    //占位符数组
    private static String[] PLACEHOLDER = {"${evaluationProjectName}", "${ticketName}", "${guide}", "${attention}", "${evaluationCode}", "${createdDate}", "${quickMark}"};
    //图片占位符单独处理
    private static String[] PICTURE_PLACEHOLDER = {"${quickMark}"};
    
    private static int WIDTH = 100; //100%
    private static int HEIGHT = 100; //100%

    /**
     * 一页一码word导出
     * @param dtos 二维码数据列表
     * @param response
     */
    @SneakyThrows
    public static void UniqueCodeWordExport(List<TicketProject> dtos, HttpServletResponse response) {
        //生成文档的名称列表
        List<String> fileNameList = new ArrayList<>();
        for (TicketProject dto : dtos) {
            String filePath = "template/UniqueCodeWordTemplate.docx"; //模板地址
            InputStream fis = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
            XWPFDocument doc = new XWPFDocument(fis);
            Iterator<XWPFParagraph> itPara = doc.getParagraphsIterator();
            while (itPara.hasNext()) {
                XWPFParagraph paragraph = (XWPFParagraph) itPara.next();
                List<String> placeholderList = Arrays.asList(PLACEHOLDER);
                placeholderList.forEach(s->{
                    List<XWPFRun> runs=paragraph.getRuns();
                    //文字替换
                    for (int i = 0; i < runs.size(); i++) {
                        //获取字符
                        String text = runs.get(i).getText(runs.get(i).getTextPosition());
                        if (text != null && text.contains("$")) {
                            //包含占位符的字符缓存
                            StringBuilder cache = new StringBuilder(text);
                            //记录run结束的角标,开始的角标为i
                            int endIndex = 0;
                            boolean contains = text.contains("}");
                            //同一个run中是否包含完成占位符
                            if (!contains) {
                                int j = i + 1 ;
                                for (; j < runs.size(); j++) {
                                    String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
                                    if (text1 == null) {
                                        continue;
                                    }
                                    cache.append(text1);
                                    if (text1.contains("}")) {
                                        endIndex = j;
                                        break;
                                    }
                                }
                            }
                            if (contains || endIndex != 0) {
                                //处理替换
                                String key = cache.toString(); //这里是完整的占位符
                                if (key.contains(s)) {
                                    if (key.contains(PICTURE_PLACEHOLDER[0])) { //替换图片
                                        InputStream in = null;

                                        String evaluationQrcode = dto.getEvaluationQrcode();
                                        String type = evaluationQrcode.substring(evaluationQrcode.lastIndexOf(".") + 1); //二维码图片后缀
                                        //创建Random类对象
                                        Random random = new Random();
                                        //产生随机数
                                        int number = random.nextInt(999) + 1;
                                        try {
                                            if (endIndex == 0) {
                                                paragraph.removeRun(endIndex); //endIndex为0时,直接删掉run
                                            }else {
                                                for (int j = endIndex; j > i; j--) {
                                                    //角标移除后,runs会同步变动,直接继续处理i就可以
                                                    paragraph.removeRun(j);
                                                }
                                            }
                                            //重新创建一个run,用来放二维码图片
                                            XWPFRun run = paragraph.createRun();

                                            in = new FileInputStream(evaluationQrcode);//设置图片路径
                                            run.addPicture(in, getPictureType(type), "quickMark" + number,
                                                    Units.toEMU(WIDTH), Units.toEMU(HEIGHT)); //图片写入
                                            break;
                                        } catch (InvalidFormatException | IOException e) {
                                            e.printStackTrace();
                                        } finally {
                                            try {
                                                if (in != null) {
                                                    in.close();
                                                }
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    }else { //文字替换
                                        /**
                                         * 参数0表示生成的文字是要从哪一个地方开始放置,设置文字从位置0开始
                                         * 就可以把原来的文字全部替换掉了
                                         */
                                        runs.get(i).setText(getValue(dto, s), 0);
                                        for (int j = endIndex; j > i; j--) {
                                            //角标移除后,runs会同步变动,直接继续处理i就可以
                                            paragraph.removeRun(j);
                                        }
                                        break;
                                    }
                                    
                                }
                            }
                        }
                    }
                });
            }
            
            String strUUID = UUIDUtils.getStrUUID();
            String fileName = strUUID+".docx";
            fileNameList.add(fileName);
            FileOutputStream outStream = null;
            outStream = new FileOutputStream("D:/file/evaluation/word/"+fileName); //单个文件保存路径
            doc.write(outStream);
            outStream.close();
        }
        //合并word
        if (fileNameList.size() > 0) {
            String mainFileName = fileNameList.get(0);
            FileInputStream fis = new FileInputStream("D:/file/evaluation/word/"+mainFileName);
            XWPFDocument doc = new XWPFDocument(fis);
            for (int i = 1; i < fileNameList.size(); i++) { //循环合并
                FileInputStream fisTail = new FileInputStream("D:/file/evaluation/word/"+fileNameList.get(i));
                XWPFDocument docTail = new XWPFDocument(fisTail);
                doc = mergeWord(doc, docTail);
            }
            //TODO 以下写文件改为使用response下载文件
            FileOutputStream outStream = null;
            outStream = new FileOutputStream("D:/file/evaluation/word/合并文件.docx");
            doc.write(outStream);
            outStream.close();
            
            //TODO 删除fileNameList列表的文件
            
        }
    }

    private static String getValue(TicketProject dto, String s) {
        if (s.equals("${evaluationProjectName}")) {
            return dto.getEvaluationProjectName();
        }else if (s.equals("${ticketName}")) {
            return dto.getTicketName();
        }else if (s.equals("${guide}")) {
            return dto.getGuide();
        }else if (s.equals("${attention}")) { //注意事项
            return dto.getAttention();
        }else if (s.equals("${evaluationCode}")) {
            return dto.getEvaluationCode();
        }else if (s.equals("${createdDate}")) {
            return dto.getCreatedDate();
        }else return null;
    }
    /**
     * 根据图片类型,取得对应的图片类型代码 
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType){
        int res = Document.PICTURE_TYPE_PICT;
        if(picType != null){
            if(picType.equalsIgnoreCase("png")){
                res = Document.PICTURE_TYPE_PNG;
            }else if(picType.equalsIgnoreCase("dib")){
                res = Document.PICTURE_TYPE_DIB;
            }else if(picType.equalsIgnoreCase("emf")){
                res = Document.PICTURE_TYPE_EMF;
            }else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
                res = Document.PICTURE_TYPE_JPEG;
            }else if(picType.equalsIgnoreCase("wmf")){
                res = Document.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

    /**
     * 将两个word文档合并
     * @param document
     * @param doucDocument2
     * @return 合并后的文档
     */
    public static XWPFDocument mergeWord(XWPFDocument document,XWPFDocument doucDocument2) {
        XWPFParagraph p = document.createParagraph();
        //设置分页符
        p.setPageBreak(true);
        CTBody src1Body = document.getDocument().getBody();
        CTBody src2Body = doucDocument2.getDocument().getBody();
//      XWPFParagraph p2 = src2Document.createParagraph(); 
        XmlOptions optionsOuter = new XmlOptions();
        optionsOuter.setSaveOuter();
        String appendString = src2Body.xmlText(optionsOuter);
        String srcString = src1Body.xmlText();
        String prefix = srcString.substring(0,srcString.indexOf(">")+1);
        String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
        String sufix = srcString.substring( srcString.lastIndexOf("<") );
        String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
        CTBody makeBody = null;
        try {
            makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
        } catch (XmlException e) {
            e.printStackTrace();
        }
        src1Body.set(makeBody);
        return document;
    }

    
}

main方法调用代码如下:

    
    public static void main(String[] args) throws Exception {
        String line = System.getProperty("line.separator");
        List<TicketProject> entitys = new ArrayList<>();
        entitys.add(TicketProject
                .builder()
                .evaluationCode("VvjiN63o9m")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("领导班子")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2022年12月31日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682057427848.jpg") //图片地址
                .createdDate("2023年03月09日")
                .build());

        entitys.add(TicketProject
                .builder()
                .evaluationCode("VvjiN6678o")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("领导班子")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682057427848.jpg")
                .createdDate("2023年03月11日")
                .build());
        
        //合并导出word文档
        UniqueCodeWordExport(entitys, null);
    }
3.导出模板

来看下模板中的站位符,如下图所示:

在这里插入图片描述
在模板提前设置好格式和样式,数据替换占位符之后就不需要再设置样式了。

图片替换占位符比较特殊,不能像文字那样,直接替换掉占位符就可以。需要先清掉图片占位符,重新创建一个run再将图片放进去。

Excel图片插入到对应单元格

同样,先看导出效果图,如下图所示:

在这里插入图片描述
这里没有在程序里面设置单元格格式,可以根据自己的需求进行调整。

1.Excel导出处理方法
	private static String[] VOTE_ROLE = {"领导班子", "外部董事", "中层"};

	/**
     * 一页一码excel导出
     * @param dtos 二维码数据列表
     * @param response
     */
    public static void UniqueCodeExcelExport(List<TicketProject> dtos, HttpServletResponse response) {
        String filePath = "template/UniqueCodeExcelTemplate.xls"; //模板地址
        InputStream fis = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
        try {
            if (dtos == null || dtos.size() < 1) {
                throw new RuntimeException("没有可导出的数据");
            }
            //根据票种角色进行分组
            Map<String, List<TicketProject>> TicketNameGroupList = dtos.stream().filter(item-> StringUtils.isNotBlank(item.getTicketName())).collect(Collectors.groupingBy(TicketProject::getTicketName));
            HSSFWorkbook workbook = new HSSFWorkbook(fis);
            HSSFSheet sheet = workbook.getSheetAt(0);
            int excelPage = 0;
            for (String s : VOTE_ROLE) {
                excelPage++; //第一页,遍历到下一个类型时,换下一页
                List<TicketProject> ticketProjects = TicketNameGroupList.get(s); //获取某一个类型的所有数据
                
                if (ticketProjects != null && ticketProjects.size() > 0) {
                    int currentPage;
                    //手工分页,一行3条,一行数据为一页
                    int totalRecord = ticketProjects.size();
                    int totalPage = totalRecord % 3;
                    if (totalPage > 0) {
                        totalPage = totalRecord / 3 + 1;
                    } else {
                        totalPage = totalRecord / 3;
                    }
                    int currentRow = 0; //每一行二维码的起始位置
                    for (int i=1; i<=totalPage; i++) { //页数(list的页数,每3条一页)
                        currentPage = i;
                        int fromIndex = 3 * (currentPage - 1);
                        int toIndex = Math.min(3 * currentPage, totalRecord);
                        //9条数据换下一页
                        int Cumulative = currentPage % 3;
                        if (Cumulative == 0) {
                            excelPage++;
                        }
                        if(currentPage != 1 && ((currentPage-1) % 3)==0){ //第四行数据开始+1
                            currentRow++; //加上分类那一行,纠正行数
                        }
                        int rowNum = (excelPage-1)*22;//每一页开始的行数
                        
                        List<TicketProject> page = ticketProjects.subList(fromIndex, toIndex);
                        //首行为分类
                        if (currentPage == 1 || Cumulative == 0) {
                            Row row1 = sheet.getRow(rowNum);
                            Cell row1cell = row1.createCell(0);
                            row1cell.setCellValue(page.get(0).getTicketName());
                        }
                        //计算行数
                        if (currentPage == 1){
                            currentRow = rowNum;
                        }
                        if (currentPage != 1) {
                            currentRow += 7;
                        }
                        for (int j=0; j<page.size(); j++) {
                            TicketProject ticketProject = page.get(j);
                            //计算cellNum 方法比较笨,但是实用
                            int cellNum;
                            if (j==0) {
                                cellNum = 1;
                            }else if (j==1) {
                                cellNum = 4;
                            }else {
                                cellNum = 7;
                            }

                            //第二行二维码
                            String url = ticketProject.getEvaluationQrcode(); //图片路径
                            String type = url.substring(url.lastIndexOf(".") + 1); //二维码图片后缀
                            InputStream in = new FileInputStream(url);
                            byte[] btImg = null;
                            try {
                                btImg = readInputStream(in);// 得到图片的二进制数据
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
                            /**
                             * dx1:左边偏移量
                             * dy1:顶部偏移量
                             */
                            HSSFClientAnchor anchor = new HSSFClientAnchor(50, 10, 0, 0, (short) cellNum, currentRow+1, (short) (cellNum+1), currentRow+2);
                            assert btImg != null;
                            Picture picture = patriarch.createPicture(anchor, workbook.addPicture(btImg, getPictureType(type)));
                            picture.resize(0.95,0.95); //设置了图片大小,就不能跟随单元格变化了(不设置这个设置不了偏移量),没有找到解决方案
                            
                            //第三行
                            Row row3 = sheet.getRow(currentRow+2);
                            Cell row3cell = row3.createCell(cellNum);
                            row3cell.setCellValue(ticketProject.getTicketName());
                            //第四行
                            Row row4 = sheet.getRow(currentRow+3);
                            Cell row4cell = row4.createCell(cellNum);
                            row4cell.setCellValue(ticketProject.getEvaluationProjectName());
                            //第五行
                            Row row5 = sheet.getRow(currentRow+4);
                            Cell row5cell = row5.createCell(cellNum);
                            row5cell.setCellValue("序列码:"+ticketProject.getEvaluationCode());
                            //第六行
                            Row row6 = sheet.getRow(currentRow+5);
                            Cell row6cell = row6.createCell(cellNum);
                            row6cell.setCellValue(ticketProject.getDescribe());
                        }
                    }
                }
            }

            String strUUID = UUIDUtils.getStrUUID();
            String fileName = strUUID+".xls";
            FileOutputStream outStream = null;
            outStream = new FileOutputStream("D:/file/evaluation/excel/"+fileName); //单个文件保存路径
            workbook.write(outStream);
            outStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
	/**
     * 从输入流中获取字节流数据
     *
     * @param inStream 输入流
     * @return
     * @throws Exception
     */
    public static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[10240];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        inStream.close();
        return outStream.toByteArray();
    }
2.main方法调用
public static void main(String[] args) throws Exception {
        String line = System.getProperty("line.separator");
        List<TicketProject> entitys = new ArrayList<>();
        entitys.add(TicketProject
                .builder()
                .evaluationCode("VvjiN63o9m")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("领导班子")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2022年12月31日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682057427848.jpg")
                .createdDate("2023年03月09日")
                .build());

        entitys.add(TicketProject
                .builder()
                .evaluationCode("VvjiN6678o")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("领导班子")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682057427848.jpg")
                .createdDate("2023年03月11日")
                .build());

        entitys.add(TicketProject
                .builder()
                .evaluationCode("VvjiN25n11")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("领导班子")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682238606868.jpg")
                .createdDate("2023年03月11日")
                .build());

        entitys.add(TicketProject
                .builder()
                .evaluationCode("Vbime1877a")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("领导班子")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682238606868.jpg")
                .createdDate("2023年03月11日")
                .build());

        entitys.add(TicketProject
                .builder()
                .evaluationCode("Vbime18N2a")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("外部董事")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682238606868.jpg")
                .createdDate("2023年03月11日")
                .build());
        
        entitys.add(TicketProject
                .builder()
                .evaluationCode("Vbime18N2a")
                .evaluationProjectName("2023年×××民主测评")
                .ticketName("外部董事")
                .describe("请扫一扫二维码或在手机和电脑浏览器上输入网址进行打分")
                .guide("欢迎参加2023年×××民主测评,本次测评采用匿名方式,不收集任何个人信息,目的在于合理、真实评价有关人员的素质能力和实际工作情况。请结合现场述职及书面述职的情况,根据您日常工作中的观察、了解和所掌握的情况,做出客观评价。")
                .attention("1.请务必在限定的时间内完成评价并提交,评价通道将于2023年3月01日关闭;" +line+
                        "    2.中途因故退出,可用同一手机再次扫码进入考评系统;" +line+
                        "    3.评价一经提交无法进行修改;" +line+
                        "    4.如果您遇到任何问题,请与党委组织部联系,非常感谢您的支持!")
                .evaluationQrcode("D:/file/evaluation/1682238606868.jpg")
                .createdDate("2023年03月11日")
                .build());
        //加测试数据
        List<TicketProject> list = new ArrayList<>(entitys);
        entitys.addAll(list);
        entitys.addAll(list);
        entitys.addAll(list);
        entitys.addAll(list);
        entitys.addAll(list);
        
        //excel导出
        UniqueCodeExcelExport(entitys, null);
    }

懒人造数据,不要吐槽我!

Excel导出有要求,9条数据为一页,一行三条数据。同一个分类的数据,分页之后要在前面加上分类名称,所以中间计算开始行数的时候,要加上这一行。

3.Excel模板

在这里插入图片描述

不想在代码里面设置格式样式,提前画好直接取模板用就好

因为在做这两个工具的时候找了很多网上的代码,感觉都不完整,而且都没有能达到我想要的效果。
在这里发出来记录一下,也分享给大家,希望对你有用!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
使用Spring BootEasyPOI导出Excel文件的步骤如下: 1. 在pom.xml文件添加EasyPOI的依赖: ``` <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.2.0</version> </dependency> ``` 2. 创建一个POJO类,用于表示Excel的每一行数据。 3. 在Controller创建一个请求处理方法,用于处理导出Excel文件的请求。在该方法使用EasyPOI创建Excel文件并将其写入输出流,然后返回一个ResponseEntity对象,该对象包含Excel文件的数据和响应头信息。 以下是一个示例Controller方法: ``` @GetMapping("/export") public ResponseEntity<byte[]> exportExcel() throws IOException { // 创建Excel文件 List<MyData> dataList = getDataList(); Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), MyData.class, dataList); // 将Excel文件写入输出流 ByteArrayOutputStream out = new ByteArrayOutputStream(); workbook.write(out); // 设置响应头信息 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDisposition(ContentDisposition.attachment().filename("data.xlsx").build()); return new ResponseEntity<>(out.toByteArray(), headers, HttpStatus.OK); } private List<MyData> getDataList() { // 返回需要导出的数据的集合 } ``` 在这个示例,我们首先创建了一个MyData类,用于表示Excel的每一行数据。然后,在Controller创建了一个请求处理方法,该方法使用EasyPOI创建Excel文件,并将其写入输出流。最后,我们设置了响应头信息,将Excel文件作为响应体返回给客户端。 希望这个示例能够帮助您理解如何使用Spring BootEasyPOI导出Excel文件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值