java实现对excel模版填充保存到本地后合并单元格并通过网络下载

我的做法是把模板填充后保存到本地,使用填充单元格工具进行处理合并

合并excel单元格工具链接:https://blog.csdn.net/weixin_45559862/article/details/156936449?sharetype=blogdetail&sharerId=156936449&sharerefer=PC&sharesource=weixin_45559862&spm=1011.2480.3001.8118

处理前:

合并后:

使用exportExcelByFillInHeBing方法

java 复制代码
package org.springblade.modules.api.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springblade.core.tool.utils.Func;
import org.springframework.core.io.ClassPathResource;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

public class ExcelMergeUtil implements CellWriteHandler {
    private int[] mergeColumnIndex;
    private int mergeRowIndex;

    public ExcelMergeUtil() {
    }

    public ExcelMergeUtil(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();

        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < mergeColumnIndex.length; i++) {
                if (curColIndex == mergeColumnIndex[i]) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }


    /**
     * 当前单元格向上合并
     *
     * @param writeSheetHolder
     * @param cell             当前单元格
     * @param curRowIndex      当前行
     * @param curColIndex      当前列
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        // 创建DataFormatter
        DataFormatter formatter = new DataFormatter();
        // 修改这行:使用formatter安全获取值
        String curData = formatter.formatCellValue(cell);
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        // 修改这行:使用formatter安全获取值
        String preData = formatter.formatCellValue(preCell);
        // 将当前单元格数据与上一个单元格数据比较
        Boolean dataBool = preData.equals(curData);
        //此处需要注意:因为我是按照主体名称确定是否需要合并的,所以获取每一行第二列数据和上一行第一列数据进行比较,如果相等合并,getCell里面的值,是主体名称所在列的下标,不能大于需要合并列数组的第一个下标
        // 修改这行:使用formatter安全获取值
        Boolean bool = formatter.formatCellValue(cell.getRow().getCell(0))
                .equals(formatter.formatCellValue(cell.getSheet().getRow(curRowIndex - 1).getCell(0)));
        if (dataBool && bool) {
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }

    /**
     * 头部样式
     *
     * @return
     */
    public static WriteCellStyle getHeadWriteCellStyle() {
        // 这里需要设置不关闭流
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置背景颜色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        //设置头字体
        WriteFont headWriteFont = new WriteFont();
        //字体大小
        headWriteFont.setFontHeightInPoints((short) 13);
        //是否加粗
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        //设置头居中
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        return headWriteCellStyle;
    }

    /**
     * 内容样式
     *
     * @return
     */
    public static WriteCellStyle getContentWriteCellStyle() {
        //内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //自动换行
        contentWriteCellStyle.setWrapped(true);
        //垂直居中
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置左边框
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        //设置右边框
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        //设置上边框
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        //设置下边框
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        return contentWriteCellStyle;
    }

    /**
     * 合并单元格导出excel工具
     *
     * @param response         响应头
     * @param fileName         文件名称
     * @param lsit             需要导出的数据
     * @param data             对应的excel导出类
     * @param mergeColumeIndex 需要合并的下标
     * @param mergeRowIndex    从第几行开始合并
     * @throws IOException
     */
    public static void exportExcel(HttpServletResponse response, String fileName, List lsit, Class data, int[] mergeColumeIndex, int mergeRowIndex) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileNamePath = URLEncoder.encode(fileName + System.currentTimeMillis(), "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileNamePath + ".xlsx");
        // 调用合并单元格工具类,此工具类是根据工程名称相同则合并后面数据
        ExcelMergeUtil excelFillCellMergeStrategy = null;
        //是否需要合并
        if (Func.isNotEmpty(mergeColumeIndex) && mergeRowIndex != -1) {
            excelFillCellMergeStrategy = new ExcelMergeUtil(mergeRowIndex, mergeColumeIndex);
        }

        //头部样式
        WriteCellStyle headWriteCellStyle = ExcelMergeUtil.getHeadWriteCellStyle();
        //内容样式
        WriteCellStyle contentWriteCellStyle = ExcelMergeUtil.getContentWriteCellStyle();

        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        EasyExcel.write(response.getOutputStream(), data)
                .registerWriteHandler(horizontalCellStyleStrategy)
                .registerWriteHandler(excelFillCellMergeStrategy)
                //sheet显示的名字
                .autoCloseStream(Boolean.TRUE).sheet(fileName)
                .doWrite(lsit);
    }

    /**
     * 根据模版导出Excel
     *
     * @param response        输出流
     * @param dataList        数据
     * @param templatePath    模版的地址
     * @param exportExcelName 导出的名称
     * @throws IOException 异常
     */
    public static void exportExcelByTemplate(HttpServletResponse response, List dataList, String templatePath, String exportExcelName) throws IOException {
        String fileName = URLEncoder.encode(exportExcelName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        ClassPathResource resource = new ClassPathResource(templatePath);
        EasyExcel.write(response.getOutputStream()).withTemplate(resource.getInputStream()).sheet().doFill(dataList);
    }

    //***************************************excel填充模版用*************************************************

    /**
     * excel填充模版用
     *
     * @param response         响应
     * @param templateFilePath 模版路径
     * @param inputFileName    导出文件名
     * @param List             数据列表
     * @param map              单个对象
     * @param sheetAt          第几个sheet
     * @param row              获取第几行的高度
     * @param mergeColumeIndex 需要合并的下标
     * @param mergeRowIndex    从第几行开始合并
     * @param fillInIndex      从第几行开始使用自定义样式
     * @param endIndex         结束合并的下标
     * @param fontSize         字体大小
     * @param endRow           样式结束行
     * @throws Exception
     */
    public static void exportExcelByFillIn(HttpServletResponse response, String templateFilePath, String inputFileName,
                                           List<?> List, int[] mergeColumeIndex, int mergeRowIndex) throws Exception {
        /*String fileNamePath = URLEncoder.encode(inputFileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileNamePath + ".xlsx");
        // 调用合并单元格工具类,此工具类是根据工程名称相同则合并后面数据
        ExcelMergeUtil excelFillCellMergeStrategy = null;
        //是否需要合并
        if (Func.isNotEmpty(mergeColumeIndex) && mergeRowIndex != -1) {
            excelFillCellMergeStrategy = new ExcelMergeUtil(mergeRowIndex, mergeColumeIndex);
        }

        //头部样式
        WriteCellStyle headWriteCellStyle = ExcelMergeUtil.getHeadWriteCellStyle();
        //内容样式
        WriteCellStyle contentWriteCellStyle = ExcelMergeUtil.getContentWriteCellStyle();

        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        InputStream is = getInputStream(templateFilePath);
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(is)
                .registerWriteHandler(horizontalCellStyleStrategy)
                .registerWriteHandler(excelFillCellMergeStrategy)
                .excelType(ExcelTypeEnum.XLSX)
                .build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        // 填充列表数据
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        excelWriter.fill(List, fillConfig, writeSheet);
        excelWriter.finish();*/
        String fileName = URLEncoder.encode(inputFileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        ClassPathResource resource = new ClassPathResource(templateFilePath);
        EasyExcel.write(response.getOutputStream()).withTemplate(resource.getInputStream()).sheet().doFill(List);

    }

    /**
     * excel填充模版用
     *
     * @param response         响应
     * @param templateFilePath 模版路径
     * @param inputFileName    导出文件名
     * @param list             数据列表
     * @param brokerColumn     经纪人列(字母表示,如"B")
     * @param mergeColumns     需要合并的列(字母表示,如"C,D,E,F,G,H,I,J,K")
     * @param startRow         起始行(从0开始)
     * @throws Exception
     */
    public static void exportExcelByFillInHeBing(HttpServletResponse response, String templateFilePath, String inputFileName, String localPath,
                                                 List<?> list, String brokerColumn, String mergeColumns, int startRow, String dependencyRules) throws Exception {
        // 如果未指定本地路径,使用默认路径
        if (localPath == null || localPath.isEmpty()) {
            // 获取用户主目录
            String userHome = System.getProperty("user.home");
            // 创建默认下载目录
            File downloadDir = new File(userHome, "Downloads");
            if (!downloadDir.exists()) {
                downloadDir.mkdirs();
            }
            localPath = downloadDir.getAbsolutePath();
        }

        // 确保路径存在
        File dir = new File(localPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        //保存到本地文件路径
        String fileName = localPath + inputFileName + ".xlsx";

        // 读取模板文件
        ClassPathResource resource = new ClassPathResource(templateFilePath);

        // 写入到当前目录
        try (FileOutputStream fos = new FileOutputStream(fileName)) {
            EasyExcel.write(fos)
                    .withTemplate(resource.getInputStream())
                    .sheet()
                    .doFill(list);
        }
        System.out.println("文件已保存到: " + new File(fileName).getAbsolutePath());
        String absolutePath = new File(fileName).getAbsolutePath();
        ExcelMergeByBroker.quickMergeWithDependency(absolutePath, brokerColumn, mergeColumns, startRow, dependencyRules);
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + inputFileName + ".xlsx");
        // 4. 读取文件并写入响应
        File file = new File(absolutePath);
        try (InputStream is = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
        }
    }

    /**
     * 获取流
     *
     * @param templateFilePath
     * @return
     * @throws Exception
     */
    public static InputStream getInputStream(String templateFilePath) throws Exception {
        ClassPathResource res = new ClassPathResource(templateFilePath);
        InputStream is = res.getInputStream();
        return is;
    }
}
java 复制代码
//使用
//每日统计
            String brokerColumn = "B";
            //需要合并的列
            String mergeColumns = "A,C,D,E,F,G,H,I,N";
            //依赖规则,格式:"H:G,N:G" 表示H列依赖G列,N列依赖G列
            String dependencyRules = "H:G,N:G";
            //从第三行后开始合并
            int startRow = 2;
            ExcelMergeUtil.exportExcelByFillInHeBing(response, "templates/数据统计模板.xlsx",
                    System.nanoTime() + "AgentWater", localPath + "/AgentWater/", dataList, brokerColumn, mergeColumns, startRow, dependencyRules);
相关推荐
Zoey的笔记本2 小时前
「软件开发缺陷管理工具」的闭环追踪设计与自动化集成架构
java·服务器·前端
setary03012 小时前
c++泛型编程之Typelists
开发语言·c++
极客先躯2 小时前
高级java每日一道面试题-2025年5月09日-基础篇[协议-注解-缓存]-JCache(JSR-107)是什么?它的主要目标是什么?
java·spring·缓存
u0104058362 小时前
Java应用的链路追踪:实现分布式跟踪
java·开发语言·分布式
这是个栗子2 小时前
【API封装参数传递】params 与 API 封装
开发语言·前端·javascript·data·params
27669582922 小时前
vercel 安全检测逆向 x-vercel-challenge-solution
开发语言·python·solution·vercel-solution·x-vercel·vercel逆向·ensun
傻乐u兔2 小时前
C语言初阶————调试实用技巧1
c语言·开发语言
予枫的编程笔记2 小时前
【Java版本】深度解析:不同版本JDK的核心区别与主流版本流行原因
java·jdk
Sammyyyyy2 小时前
Gemini CLI 进阶:构建安全的MCP连接与验证策略
开发语言·ai·ai编程·servbay