当前位置: 首页 > article >正文

【EasyExcel】EasyExcel导出表格包含合计行、自定义样式、自适应列宽

目录

  • 0 EasyExcel简介
  • 1 Excel导出工具类
    • 设置自定义表头样式
    • 设置自适应列宽
    • 添加合计行
  • 2 调用导出工具类导出Excel表
  • 3 测试结果

0 EasyExcel简介

在数据处理和报表生成的过程中,Excel是一个非常常用的工具。特别是在Java开发中,EasyExcel库因其简单高效而备受欢迎。

EasyExcel是阿里巴巴开源的一个Excel处理库,特别适用于大数据量的Excel文件的读写。它的优点在于使用简单、性能高效,特别适合在Java项目中处理Excel文件。

1 Excel导出工具类

Excel导出工具类ExcelUtil类代码如下:

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

@Data
public class ExcelUtil{

	public static void downloadExcelWithHead(HttpServletResponse response, List<?> list, String sheetName, List<List<String>> rowHead) throws IOException {
	        if (list.isEmpty()) {
	            throw new IllegalArgumentException("列表不能为空!");
	        }
	
	        Class<?> clazz = list.get(0).getClass();
	        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
	        response.setCharacterEncoding("utf-8");
	        String fileName = URLEncoder.encode(sheetName, "UTF-8").replaceAll("\\+", "%20");
	        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
	        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), clazz).excelType(ExcelTypeEnum.XLSX).build();
	
	        // 创建 ExcelWriterBuilder
	        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream(), clazz)
	                .autoCloseStream(true)
	                .needHead(true);
	
	        excelWriterBuilder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
	        excelWriterBuilder.useDefaultStyle(false);
	
	        // 创建 WriteSheet 并设置动态标题行
	        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
	                .head(rowHead) // 设置标题行
	                .registerWriteHandler(setCellStyle()) // 设置表头样式
	                .registerWriteHandler(new CustomCellWriteUtil()) // 设置列宽自适应
	                .build();
	
	        // 写入数据
	        excelWriter.write(list, writeSheet);
	        excelWriter.finish();
	    }
	}

设置自定义表头样式

其中,downloadExcelWithHead()中调用setCellStyle()方法用于设置表头样式。setCellStyle()方法的代码如下:

    public static HorizontalCellStyleStrategy setCellStyle() {
        // 表头策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为白色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.index);
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        //边框
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        //自动换行
        headWriteCellStyle.setWrapped(true);
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setBold(true);
        headWriteFont.setFontName("宋体");
        headWriteFont.setFontHeightInPoints((short)12);
        headWriteCellStyle.setWriteFont(headWriteFont);

        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
        contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        // 背景白色
        contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //边框
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        //自动换行
        contentWriteCellStyle.setWrapped(true);
        //文字
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short)12);
        contentWriteFont.setFontName("宋体");
        contentWriteCellStyle.setWriteFont(contentWriteFont);

        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

设置自适应列宽

自适应列宽通过CustomCellWriteUtil类设置,CustomCellWriteUtil类的代码如下:

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * excel自适应列宽
 */
public class CustomCellWriteUtil extends AbstractColumnWidthStyleStrategy {
    private static final int MAX_COLUMN_WIDTH = 255;
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);

    public CustomCellWriteUtil() {
    }

    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = (Map) CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap(16);
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }
                Integer maxColumnWidth = (Integer) ((Map) maxColumnWidthMap).get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    ((Map) maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 7250);
                }
            }
        }
    }

    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = (CellData) cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

添加合计行

合计行用于展示数据的总和或其他统计信息。在Controller中调用downloadExcelWithHead()方法导出Excel时,添加合计行和自定义表头形式。

2 调用导出工具类导出Excel表

此处以导出产品明细表为例,新建产品表如下:

在这里插入图片描述

entity、mapper、service层省略,在Controller层中添加一个导出产品明细表接口,代码如下:

import com.z.entity.Product;
import com.z.service.ProductService;
import com.z.util.ConvertUtil;
import com.z.util.ExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private ProductService productService;

    @PostMapping("/export")
    public void export(HttpServletResponse response) throws IOException {
        List<Product> dataList = productService.list();
        if(null == dataList || dataList.isEmpty()){
            throw new RuntimeException("数据为空,导出失败!");
        }
        // 计算合计
        double totalProductCost = dataList.stream()
                .mapToDouble(data -> data.getProductCost() != null ? data.getProductCost() : 0.0)
                .sum();
        int totalProductNum = dataList.stream()
                .mapToInt(data -> data.getProductNum() != null ? data.getProductNum() : 0)
                .sum();
        // 创建合计行
        Product totalRow = new Product();
        totalRow.setId("合计");
        totalRow.setProductCost(totalProductCost);
        totalRow.setProductNum(totalProductNum);

        // 添加合计行到列表
        dataList.add(totalRow);

        // 创建自定义表头
        List<List<String>> rowHead = createHead();

        // 导出 Excel(带标题列,带合计列)
        ExcelUtil.downloadExcelWithHead(response, dataList, "产品明细表", rowHead);
    }

    /**
     * 设置Excel头
     * @return
     */
    private List<List<String>> createHead() {
        List<List<String>> list = new ArrayList<>();

        // 第一列表头
        List<String> headRow1 = new ArrayList<>();
        headRow1.add("产品明细表报表");
        headRow1.add("test");
        headRow1.add("产品序号");
        list.add(headRow1);

        // 第二列表头
        List<String> headRow2 = new ArrayList<>();
        headRow2.add("产品明细表报表");
        headRow2.add("备注:"+"此处可调用方法取值");
        headRow2.add("产品编号");
        list.add(headRow2);

        // 第三列表头
        List<String> headRow3 = new ArrayList<>();
        headRow3.add("产品明细表报表");
        headRow3.add("备注:"+"此处可调用方法取值");
        headRow3.add("产品名称");
        list.add(headRow3);

        // 第四列表头
        List<String> headRow4 = new ArrayList<>();
        headRow4.add("产品明细表报表");
        headRow4.add("无");
        headRow4.add("产品进价");
        list.add(headRow4);

        // 第五列表头
        List<String> headRow5 = new ArrayList<>();
        headRow5.add("产品明细表报表");
        headRow5.add("测试");
        headRow5.add("产品库存");
        list.add(headRow5);

        return list;
    }

其中,在使用createHead()方法设置Excel头时,可以调用service层中的方法,自定义显示特定值。

3 测试结果

最终使用postman调用该导出接口,导出的表格结果如下:

在这里插入图片描述


http://www.kler.cn/a/382471.html

相关文章:

  • 【无标题】123
  • 查缺补漏----用户上网过程(HTTP,DNS与ARP)
  • spark-on-k8s 介绍
  • 10大软件使用感受分享,数据恢复的得力助手!!
  • 疯狂Java讲义-Java基础类库
  • 基于YOLO11/v10/v8/v5深度学习的老鼠智能检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
  • Rust 构建 TCP/UDP 网络服务
  • 导航栏渐变色iOS
  • 在不知道root密码的情况下向MobaXterm发送信息
  • 机器学习在运维中的应用
  • 仓库(Repository)
  • Go + Wasm
  • 深入理解 C++ 中的 std::vector
  • 淘宝商品详情 API:助力电商业务腾飞的新桥梁
  • 流程与模式
  • Python正则表达式匹配汉字、英文、数字、常用符号等
  • Automated Isotope Identification Algorithm UsingArtificial Neural Networks-论文阅读
  • Rust常用数据结构教程 String与str,元组和数组
  • 【K8S系列】Kubernetes 中 Service 更改未生效的故障排查与解决方案【已解决】
  • 智能提醒助理系列-jdk8升级到21,springboot2.3升级到3.3【性能篇】
  • WandB概念、主要功能、详细说明和总结
  • 鸿蒙ArkTS中的布局容器组件(Scroll、List、Tabs)
  • react中得类组件和函数组件有啥区别,怎么理解这两个函数
  • 源文件到可执行文件流程
  • Vue.js组件开发:构建高效、可复用的前端应用
  • 【MATLAB源码-第200期】基于matlab的鸡群优化算法(CSO)机器人栅格路径规划,输出做短路径图和适应度曲线。