easypoi多级表头、多个sheet导出,动态导出列、表头

pom.xml

<!-- easypoi excel -->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>4.4.0</version>
</dependency>

一、多级表头和多个sheet

🧐 效果

因为这里是用注解实现的,只有到三级表头,如果表头超过了三级,比如有四级、五级的话,就不建议用注解,还是用模板比较方便。

🍔 代码
多级表头一个是可以用注解实现:@ExcelCollection 和 @Excel 组合使用;还有就是可以用模板实现;还有一种就是用原生poi写,这个就更麻烦了。模板实现就更灵活,很复杂的表格都可以实现,不过就是填充数据比较麻烦。这里先写用注解实现的方式。

1、实体bean
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import lombok.Data;

import java.util.List;

@Data
public class ExcelExportVo {
    
    // 这两个就是一级表头,最后一级表头对应的是具体的某个属性,它们都是被包裹在一级表头下的
    @ExcelCollection(name = "用户信息")
    private List<UserInfo> userInfoList;

    @ExcelCollection(name = "用户角色和权限")
    private List<RoleInfo> roleInfoList;

    /**
     * 用户信息
     */
    @Data
    public static class UserInfo{
        // 二级表头可以用 groupName 实现
        @Excel(name = "用户账号",width = 20,groupName = "基本信息")
        private String userName;

        @Excel(name = "用户姓名",width = 20,groupName = "基本信息")
        private String realName;

        @Excel(name = "手机号码",width = 20,groupName = "基本信息")
        private String phone;

        @Excel(name = "所在公司",width = 20,groupName = "单位部门")
        private String com;

        @Excel(name = "所在部门",width = 20,groupName = "单位部门")
        private String dept;

        public UserInfo(String userName, String realName, String phone, String com, String dept) {
            this.userName = userName;
            this.realName = realName;
            this.phone = phone;
            this.com = com;
            this.dept = dept;
        }
    }

    /**
     * 用户角色权限
     */
    @Data
    public static class RoleInfo{
        @Excel(name = "所属角色名称",width = 20,groupName = "角色")
        private String roleName;

        @Excel(name = "所属角色代码",width = 20,groupName = "角色")
        private String roleCode;

        @Excel(name = "菜单权限",width = 40,groupName = "权限")
        private String menu;

        @Excel(name = "数据权限",width = 40,groupName = "权限")
        private String data;

        public RoleInfo(String roleName, String roleCode, String menu, String data) {
            this.roleName = roleName;
            this.roleCode = roleCode;
            this.menu = menu;
            this.data = data;
        }
    }
}

2、ExcelUtil、ExcelStyleUtil
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.imports.ExcelImportService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;

/**
 * excel操作工具类
 */
public class ExcelUtil {

    /**
     * 偶数行设置背景色
     */
    private static void setRowBackground(Workbook workbook){
        Sheet sheet = workbook.getSheetAt(0);
        CellStyle styles = ExcelStyleUtil.getStyles(workbook,false,(short) 12);
        for(int i = 0; i <= sheet.getLastRowNum(); i ++) {
            Row row = sheet.getRow(i);
            if (i%2==0){
                for(int j = 0; j < row.getPhysicalNumberOfCells(); j ++) {
                    Cell cell = row.getCell(j);
                    cell.setCellStyle(styles);
                }
            }
        }
    }

    /**
     * 导出设置隔行背景色
     * @param params 导出参数
     * @param list 数据
     * @param pojoClass pojo类型
     * @param fileName 文件名称
     * @param isSetRowBackground 是否设置隔行背景色
     */
    public static void exportExcel(ExportParams params, List<?> list, Class<?> pojoClass, String fileName,boolean isSetRowBackground, HttpServletResponse response){
        Workbook workbook = ExcelExportUtil.exportExcel(params,pojoClass,list);
        if (workbook != null);
        if (isSetRowBackground) setRowBackground(workbook);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * excel 导出
     * @param list           数据
     * @param title          标题
     * @param sheetName      sheet名称
     * @param pojoClass      pojo类型
     * @param fileName       文件名称
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response){
        ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
        exportParams.setStyle(ExcelStyleUtil.class);
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 导出
     * @param list           数据
     * @param pojoClass      pojo类型
     * @param fileName       文件名称
     */
    public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response){
        ExportParams exportParams = new ExportParams();
        exportParams.setStyle(ExcelStyleUtil.class);
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * list map 导出
     * @param list     数据
     * @param fileName 文件名称
     */
    public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response){
        ExportParams exportParams = new ExportParams();
        exportParams.setStyle(ExcelStyleUtil.class);
        defaultExport(list, fileName, response);
    }

    /**
     * 默认的 excel 导出
     * @param list         数据
     * @param pojoClass    pojo类型
     * @param fileName     文件名称
     * @param exportParams 导出参数
     */
    private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams){
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * 默认的 excel 导出
     * @param list     数据
     * @param fileName 文件名称
     */
    private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response){
        Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.XSSF);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * 下载
     * @param fileName 文件名称
     * @param response
     * @param workbook excel数据
     */
    public static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook){
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + "." + ExcelTypeEnum.XLS.getValue(), "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * excel 导入
     * @param filePath   excel文件路径
     * @param titleRows  标题行
     * @param headerRows 表头行
     * @param pojoClass  pojo类型
     */
    public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setNeedSave(true);
        params.setSaveUrl("/excel/");
        try {
            return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("模板不能为空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 导入
     * @param file       上传的文件
     * @param titleRows  标题行
     * @param headerRows 表头行
     * @param needVerify 是否检验excel内容
     * @param pojoClass  pojo类型
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, boolean needVerify, Class<T> pojoClass) throws IOException {
        if (file == null) {
            return null;
        }
        try {
            return importExcel(file.getInputStream(), titleRows, headerRows, needVerify, pojoClass);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 导入
     * @param inputStream 文件输入流
     * @param titleRows   标题行
     * @param headerRows  表头行
     * @param needVerify  是否检验excel内容
     * @param pojoClass   pojo类型
     */
    public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, boolean needVerify, Class<T> pojoClass) throws IOException {
        if (inputStream == null) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setSaveUrl("upload/excel/");
        params.setNeedSave(true);
        params.setNeedVerify(needVerify);
        try {
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("excel文件不能为空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Excel 类型枚举
     */
    enum ExcelTypeEnum {
        XLS("xls"), XLSX("xlsx");
        private String value;

        ExcelTypeEnum(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }


    /**
     * 上传文件,返回一个workbook
     * @param file
     */
    public static Workbook importExcel(MultipartFile file) throws IOException {
        File toFile = new File(file.getOriginalFilename());
        Workbook workbook = null;
        if(toFile.getPath().endsWith("xls")){
            workbook = new HSSFWorkbook(file.getInputStream());
        }else if(toFile.getPath().endsWith("xlsx")){
            workbook = new XSSFWorkbook(file.getInputStream());
        }else {
            throw new RuntimeException("请确认你上传的文件类型");
        }
        return workbook;
    }

    /**
     * 读取指定sheet的数据
     * @param file 上传的文件
     * @param sheetName 要读取的sheetName
     * @param titleRows 表头行数
     * @param headRows 标题行数
     * @param startRows 表头之前有多少行不要的数据,从1开始,忽略空行
     * @param readRows 要读取多少行数据,从0开始,比如读取十行,值就是9; 不指定时默认为0
     * @param pojoClass 实体
     */
    public static <T> List<T> importExcel(MultipartFile file,String sheetName,Integer titleRows,Integer headRows, Integer startRows,Integer readRows,Class<T> pojoClass) throws Exception {
        Workbook workbook = importExcel(file);
        int numberOfSheets = workbook.getNumberOfSheets();
        List<T> list = null;
        for (int i = 0; i < numberOfSheets; i++) {
            String name = workbook.getSheetName(i).trim();
            if (name.equals(sheetName) || name.endsWith(sheetName)){
                ImportParams params = new ImportParams();
                params.setTitleRows(titleRows);
                params.setHeadRows(headRows);
                params.setStartRows(startRows);
                params.setReadRows(readRows);
                //第几个sheet页
                params.setStartSheetIndex(i);
                final ExcelImportService excelImportService = new ExcelImportService();
                ExcelImportResult<T> result = excelImportService.importExcelByIs(file.getInputStream(), pojoClass, params, false);
                list = result.getList();
                break;
            }
        }
        return list;
    }

    /**
     * 以map的形式导出表格
     * @param list 数据
     */
    public static <T> List<Map<String, Object>> objectToMap(List<T> list){
        List<Map<String, Object>> result = new ArrayList<>();
        Map<String, Object> map = null;
        try {
            for (T item : list) {
                map = new HashMap<>();
                Class<?> clazz = item.getClass();
                for (Field field : clazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    String fieldName = field.getName();
                    Object value = field.get(item);
                    map.put(fieldName, value);
                }
                result.add(map);
            }
            return result;
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 设置注解参数
     * @param annotation  注解
     * @param entity
     */
    private static void annotationParams(Excel annotation,ExcelExportEntity entity){
        if (annotation.addressList()){
            entity.setAddressList(annotation.addressList());
            entity.setReplace(annotation.replace());
        }
        entity.setReplace(annotation.replace());
        entity.setOrderNum(Integer.parseInt(annotation.orderNum()));
        entity.setGroupName(annotation.groupName());
        entity.setNeedMerge(annotation.needMerge());
        entity.setMergeVertical(annotation.mergeVertical());
    }

    /**
     * 动态导出列,根据Excel注解获取列的字段注释(表头名)、宽度
     * @param clazz
     * @param fields 选择要导出的列
     * @param changeHead 要更改表头的列,格式是{"字段1":"更改的表头1","字段2":"更改的表头2"}
     */
    public static List<ExcelExportEntity> dynamicExport(Class<?> clazz,String fields, JSONObject changeHead) {
        List<ExcelExportEntity> beanList = new ArrayList<>();
        String[] split = fields.split(",");
        int length = split.length;
        try {
            for (int i = 0; i < length; i++) {
                Field f = clazz.getDeclaredField(split[i]);
                Excel annotation = f.getAnnotation((Excel.class));
                String comment = annotation.name();
                if (changeHead != null && Objects.nonNull(changeHead.get(f.getName()))){
                    comment = changeHead.get(f.getName()).toString();
                }
                Double width = annotation.width();
                ExcelExportEntity entity = new ExcelExportEntity(comment, f.getName(), width.intValue());
                annotationParams(annotation,entity);
                beanList.add(entity);
            }
        }catch (NoSuchFieldException e){
            e.printStackTrace();
        }
        return beanList;
    }

    /**
     * 动态导出列(选择要忽略的列),根据Excel注解获取列的字段注释(表头名)、宽度
     * @param clazz
     * @param fields 选择要忽略的列
     * @param changeHead 要更改表头的列,格式是{"字段名1":"更改的表头1","字段名2":"更改的表头2"}
     */
    public static List<ExcelExportEntity> dynamicIgnoreExport(Class<?> clazz, String fields, JSONObject changeHead) {
        List<ExcelExportEntity> beanList = new ArrayList<>();
        String[] split = fields.split(",");
        int length = split.length;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields) {
            Excel annotation = f.getAnnotation((Excel.class));
            if (annotation != null){
                boolean flag = false;
                for (int i = 0; i < length; i++) {
                    if (f.getName().equals(split[i])){
                        flag = true;
                        break;
                    }
                }
                if (flag) continue;
                String comment = annotation.name();
                if (changeHead != null && Objects.nonNull(changeHead.get(f.getName()))){
                    comment = changeHead.get(f.getName()).toString();
                }
                Double width = annotation.width();
                ExcelExportEntity entity = new ExcelExportEntity(comment, f.getName(), width.intValue());
                annotationParams(annotation,entity);
                beanList.add(entity);
            }
        }
        return beanList;
    }

    /**
     * 导出Excel,并在最后追加图片
     * @param sheetName sheet名称
     * @param wb        HSSFWorkbook对象
     */
    public static Workbook getWorkbook(String sheetName,Workbook wb, String imgUrl) throws IOException {
        // 第一步,创建一个HSSFWorkbook,对应一个Excel文件
        if (wb == null) {
            wb = new HSSFWorkbook();
        }
        // 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
        Sheet sheet = wb.getSheet(sheetName);
        /*生成图表*/
        if(!StringUtils.isEmpty(imgUrl)) {
            String[] imgUrlArr = imgUrl.split("base64,");//拆分base64编码后部分
            byte[] buffer = new BASE64Decoder().decodeBuffer(imgUrlArr[1]);
            String picPath = System.getProperty("user.dir")+"\\upload\\image\\pic.png";
            File file = new File(picPath);//图片文件
            try {
                //生成图片
                OutputStream out = new FileOutputStream(file);//图片输出流
                out.write(buffer);
                out.flush();//清空流
                out.close();//关闭流
                ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // 将图片写入流中
                BufferedImage bufferImg = ImageIO.read(new File(picPath));
                ImageIO.write(bufferImg, "PNG", outStream);
                // 利用HSSFPatriarch将图片写入EXCEL
                Drawing<?> drawing = sheet.createDrawingPatriarch();
                //位置:第1个单元格中x轴的偏移量、第1个单元格中y轴的偏移量、 第2个单元格中x轴的偏移量、 第2个单元格中y轴的偏移量、第1个单元格的列号、第1个单元格的行号、 第2个单元格的列号、第2个单元格的行号
                //HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) 0, 8, (short) 10, 40);
                ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 6, 9, 40);
                drawing.createPicture(anchor, wb.addPicture(outStream.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            if (file.exists()) {
                file.delete();//删除图片
            }
        }
        return wb;
    }
}

import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.params.ExcelForEachParams;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import org.apache.poi.ss.usermodel.*;

/**
 * excel导出样式设置工具类
 */
public class ExcelStyleUtil implements IExcelExportStyler {

    private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
    private static final short FONT_SIZE_TEN = 12;
    private static final short FONT_SIZE_ELEVEN = 14;
    private static final short FONT_SIZE_TWELVE = 18;

    /**
     * 大标题样式
     */
    private CellStyle headerStyle;
    /**
     * 每列标题样式
     */
    private CellStyle titleStyle;
    /**
     * 数据行样式
     */
    private CellStyle styles;

    public ExcelStyleUtil(Workbook workbook) {
        this.init(workbook);
    }

    /**
     * 初始化样式
     * @param workbook
     */
    private void init(Workbook workbook) {
        this.headerStyle = initHeaderStyle(workbook);
        this.titleStyle = initTitleStyle(workbook,true,FONT_SIZE_ELEVEN);
        this.styles = initStyles(workbook);
    }

    @Override
    public CellStyle getHeaderStyle(short i) {
        return headerStyle;
    }

    @Override
    public CellStyle getTitleStyle(short i) {
        return titleStyle;
    }

    @Override
    public CellStyle getTemplateStyles(boolean b, ExcelForEachParams excelForEachParams) {
        return null;
    }

    @Override
    public CellStyle getStyles(boolean b, ExcelExportEntity excelExportEntity) {
        return styles;
    }

    @Override
    public CellStyle getStyles(Cell cell, int i, ExcelExportEntity entity, Object o, Object o1) {
        return getStyles(true, entity);
    }

    /**
     * 设置隔行背景色
     */
    public static CellStyle getStyles(Workbook workbook,boolean isBold,short size) {
        CellStyle style = initTitleStyle(workbook,isBold,size);
        style.setDataFormat(STRING_FORMAT);
        return style;
    }

    /**
     * 初始化--大标题样式
     */
    private static CellStyle initHeaderStyle(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
        return style;
    }

    /**
     * 初始化--每列标题样式
     */
    private static CellStyle initTitleStyle(Workbook workbook,boolean isBold,short size) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, size, isBold));
        //背景色
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return style;
    }

    /**
     * 初始化--数据行样式
     */
    private static CellStyle initStyles(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_TEN, false));
        style.setDataFormat(STRING_FORMAT);
        return style;
    }

    /**
     * 基础样式
     */
    private static CellStyle getBaseCellStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        //下边框
        style.setBorderBottom(BorderStyle.THIN);
        //左边框
        style.setBorderLeft(BorderStyle.THIN);
        //上边框
        style.setBorderTop(BorderStyle.THIN);
        //右边框
        style.setBorderRight(BorderStyle.THIN);
        //水平居中
        style.setAlignment(HorizontalAlignment.CENTER);
        //上下居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置自动换行
        style.setWrapText(true);
        return style;
    }

    /**
     * 字体样式
     * @param size   字体大小
     * @param isBold 是否加粗
     */
    private static Font getFont(Workbook workbook, short size, boolean isBold) {
        Font font = workbook.createFont();
        //字体样式
        font.setFontName("宋体");
        //是否加粗
        font.setBold(isBold);
        //字体大小
        font.setFontHeightInPoints(size);
        return font;
    }
}

3、导出
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import com.common.util.ExcelStyleUtil;
import com.common.util.ExcelUtil;
import com.entity.sys.query.ExcelExportVo;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/test")
public class TestController {

    /**
     * 多级表头、多个sheet导出
     */
    @RequestMapping("/export")
    public void export(HttpServletResponse response){
        // 模拟数据
        List<ExcelExportVo> dataList = new ArrayList<>();
        List<ExcelExportVo.UserInfo> userInfoList = new ArrayList<>();
        List<ExcelExportVo.RoleInfo> roleInfoList = new ArrayList<>();
        userInfoList.add(new ExcelExportVo.UserInfo("chenyi","陈仪","12345678910","战神联盟","科研部"));
        userInfoList.add(new ExcelExportVo.UserInfo("honger","洪洱","13241220000","前途有限公司","市场部"));
        userInfoList.add(new ExcelExportVo.UserInfo("zhangsan","张三","12544445555","前途有限公司","研发部"));
        userInfoList.add(new ExcelExportVo.UserInfo("lisi","李四","13125223561","战神联盟","科研部"));
        userInfoList.add(new ExcelExportVo.UserInfo("wangwu","王五","15423226355","战神联盟","销售部"));

        roleInfoList.add(new ExcelExportVo.RoleInfo("经理","JL","sys,dept,role,menu","view,add,update,delete"));
        roleInfoList.add(new ExcelExportVo.RoleInfo("经理","JL","sys,dept,role,menu","view,add,update,delete"));
        roleInfoList.add(new ExcelExportVo.RoleInfo("组长","ZZ","role,menu","view,add,update"));
        roleInfoList.add(new ExcelExportVo.RoleInfo("普通","PT","menu","view,add"));
        roleInfoList.add(new ExcelExportVo.RoleInfo("普通","PT","menu","view,add"));

        ExcelExportVo vo = new ExcelExportVo();
        vo.setUserInfoList(userInfoList);
        vo.setRoleInfoList(roleInfoList);
        dataList.add(vo);

        // 第一个sheet
        ExportParams params1 = new ExportParams();
        params1.setStyle(ExcelStyleUtil.class);
        params1.setSheetName("用户信息1");
        params1.setTitle("用户信息");
        Map<String, Object> params1Map = new HashMap<>();
        params1Map.put("title", params1);
        params1Map.put("entity", ExcelExportVo.class);
        params1Map.put("data", dataList);

        // 第二个sheet
        ExportParams params2 = new ExportParams();
        params2.setStyle(ExcelStyleUtil.class);
        params2.setSheetName("用户信息2");
        Map<String, Object> params2Map = new HashMap<>();
        params2Map.put("title", params2);
        params2Map.put("entity", ExcelExportVo.class);
        params2Map.put("data", new ArrayList<>());

        // 第三个sheet
        ExportParams params3 = new ExportParams();
        params3.setStyle(ExcelStyleUtil.class);
        params3.setSheetName("用户信息3");
        Map<String, Object> params3Map = new HashMap<>();
        params3Map.put("title", params3);
        params3Map.put("entity", ExcelExportVo.class);
        params3Map.put("data", new ArrayList<>());
        
        // 将3个sheet封装
        List<Map<String, Object>> sheetsList = new ArrayList<>();
        sheetsList.add(params1Map);
        sheetsList.add(params2Map);
        sheetsList.add(params3Map);
        Workbook workBook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        ExcelUtil.downLoadExcel("用户信息",response,workBook);
    }
}

二、模板导出

关于模板导出,其他倒没什么,主要是填充数据比较麻烦,此外还有一个要注意的点,就是关于数据横向遍历,也就是列遍历,就是新建列而不是新建行。

横向遍历,模板的语法是用 #fe,然后还需要在代码中手动开启列遍历。(之前我看文档上写的这个语法是横向遍历的,但是我用了一直不起作用,后面找啊找,终于在网上找到原因了)

{{#fe:headList t.name }}
String excelPath = System.getProperty("user.dir")+"\\src\\main\\resources\\template\\1.xls";
TemplateExportParams params = new TemplateExportParams(excelPath);
// 就是这一句,必须要设置为 true
params.setColForEach(true);
三、动态导出不同列、动态更改表头

就是有的时候有某列或者某几列不符合条件,不要导出。

举个🌰:
1、就比如有两个页面,它们的数据来源、字段其实都是一个表的,但是因为有一些条件,页面1只展示符合条件xx的数据,页面2只展示符合条件xxx的数据,而且展示的字段也不尽相同;

2、还有一个假如根据天数来导出数据,每一天作为列,但是每个月的天数都不一样,要怎么根据月份的天数去导出正确的列数呢?

我们可以用 ExcelExportEntity 和 @Excel 来实现。

思路:案例1因为两个页面数据都是同一个数据库表的数据,所以实体bean也是同一个。我们可以把两个页面要导出的字段都注解上@Excel,然后点击导出,调用接口的时候,控制哪个页面要导出哪几个字段(或者忽略哪几个字段不用导出)。

// 这里简单说一下,比如实体bean有这几个字段:姓名、性别、年龄、手机、邮箱。
// 页面1 要导出 姓名、性别、年龄 这几个字段;页面2 要导出 姓名、手机、邮箱 这几个字段。
// 那么在controller中,直接把这几个字段名用逗号隔开拼接就行,调用 ExcelUtil.dynamicExport 方法,会创建动态列的表头。
// 如果要导出的列太多,拼接的字符串太长,可以选择忽略列,哪几列是不用导出的,用逗号隔开拼接,调用 ExcelUtil.dynamicIgnoreExport 方法。

/**
 * 还有就是,有的时候可能同一个字段在不同页面,显示的表头不一样。
 * 比如姓名这列,在页面1表头是姓名,在@Excel注解中设置的name也是姓名,但是它在页面2的表头是用户姓名。
 * 这个时候我们就需要在页面2的导出中,改变姓名这列的表头了。在 dynamicExport、dynamicIgnoreExport 这两个方法中,设置
 * changeHead 的值为 {"name":"用户姓名"} ,其中 name 表示字段名。
 */
@Excel(name = "姓名",width = 15)
private String name;


下面这里详细讲一下案例2。

🍟 根据天数动态导出列

@Data
public class TemplateVo {
    // 这里只导出25号以后的天数(不然的话字段太多了,这里只是举个栗子,具体字段看实际情况)
    @Excel(name = "25日",width = 12)
    private String day1;
    @Excel(name = "26日",width = 12)
    private String day2;
    @Excel(name = "27日",width = 12)
    private String day3;
    @Excel(name = "28日",width = 12)
    private String day4;
    @Excel(name = "29日",width = 12)
    private String day5;
    @Excel(name = "30日",width = 12)
    private String day6;
    @Excel(name = "31日",width = 12)
    private String day7;

    public TemplateVo(String day1, String day2, String day3, String day4, String day5, String day6, String day7) {
        this.day1 = day1;
        this.day2 = day2;
        this.day3 = day3;
        this.day4 = day4;
        this.day5 = day5;
        this.day6 = day6;
        this.day7 = day7;
    }
}

/**
 * 根据年月获取需要导出的列
 */
private String getExportColumn(int year,int month){
    String column = "";
    String date = year+"-"+month+"-25";
    LocalDate parse = LocalDateTimeUtil.parseDate(date,"yyyy-M-dd");
    // 获取这个月份的长度,也就是天数
    int length = parse.lengthOfMonth();
    // 因为是导出25号(包括25号)以后的天数,所以需要 总天数-25+1
    int day = length-25+1;
    // 25号到31号总共7天,遍历7次,拼接要导出的列名
    for (int i = 1; i <= 7; i++) {
        if (i<day) column += "day"+i+",";
        else if (i==day) column += "day"+i;
    }
    return column;
}

/**
 * 导出动态列
 */
@RequestMapping("/dynamicColumnExport")
public void dynamicColumnExport(int year,int month,HttpServletResponse response){
    List<TemplateVo> list = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        list.add(new TemplateVo("25号"+i,"26号"+i,"27号"+i,"28号"+i,"29号"+i,"30号"+i,"31号"+i));
    }
    // 获取要导出的列名
    String column = getExportColumn(year, month);
    // 这里是创建动态列的表头,dynamicExport 这个方法在上面那个 ExcelUtil 有
    List<ExcelExportEntity> beanList = ExcelUtil.dynamicExport(TemplateVo.class, column);
    ExportParams params = new ExportParams();
    params.setStyle(ExcelStyleUtil.class);
    Workbook workbook = ExcelExportUtil.exportExcel(params, beanList, list);
    ExcelUtil.downLoadExcel("下载",response,workbook);
}


🌭 效果


🥞 动态更改表头

@Data
public class SysUser{
    @Excel(name = "姓名",width = 20)
    private String name;
    @Excel(name = "性别",width = 15)
    private String sex;
    @Excel(name = "年龄",width = 15)
    private Integer age;
    @Excel(name = "部门",width = 20)
    private String dept;
    @Excel(name = "职位",width = 20)
    private String post;
    @Excel(name = "电话",width = 20)
    private String phone;
    @Excel(name = "邮箱",width = 20)
    private String email;
}

/**
 * 页面1:导出 姓名、性别、年龄、手机、邮箱 这几个字段,忽略部门、职位。
 */
@GetMapping("/export")
public void export(HttpServletResponse response) {
    List<SysUser> list = sysUserService.getList();
    String fileName = "用户列表";
    List<ExcelExportEntity> exportEntityList = ExcelUtil.dynamicIgnoreExport(SysUser.class, "dept,post", null);
    ExportParams exportParams = new ExportParams();
    exportParams.setTitleHeight((short) 15);
    exportParams.setStyle(ExcelStyleUtil.class);
    Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntityList, result);
    ExcelUtil.downLoadExcel(fileName,response,workbook);
}

/**
 * 页面2:导出 姓名、手机、邮箱 这几个字段,其中姓名的表头改为用户姓名,邮箱的表头改为电子邮箱。
 */
@GetMapping("/export")
public void export(HttpServletResponse response) {
    List<SysUser> list = sysUserService.getList();
    String fileName = "用户列表";
    JSONObject obj = new JSONObject();
    obj.putOpt("name","用户姓名");
    obj.putOpt("email","电子邮箱");
    List<ExcelExportEntity> exportEntityList = ExcelUtil.dynamicExport(SysUser.class, "name,phone,email", obj);
    ExportParams exportParams = new ExportParams();
    exportParams.setTitleHeight((short) 15);
    exportParams.setStyle(ExcelStyleUtil.class);
    Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntityList, result);
    ExcelUtil.downLoadExcel(fileName,response,workbook);
}

暂时就这些了,其实做项目的时候,easypoi碰到的问题不止这些,现在项目做完了。
 

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用Easypoi导出动态多级复杂表头可以通过编写自定义的导出类来实现,以下是一个示例代码: ```java public class CustomExportUtil { /** * 导出Excel * * @param title 标题 * @param headers 表头 * @param dataList 数据表 * @param dateFormat 日期格式 * @param response HttpServletResponse对象 * @throws IOException */ public static void exportExcel(String title, List<ExcelHeader> headers, List<Map<String, Object>> dataList, String dateFormat, HttpServletResponse response) throws IOException { // 创建Excel对象 Workbook workbook = new SXSSFWorkbook(1000); // 创建Sheet对象 Sheet sheet = workbook.createSheet(title); // 设置默认sheet.setDefaultColumnWidth(15); // 创建第一行,设置表头 Row headerRow = sheet.createRow(0); int colIndex = 0; for (ExcelHeader header : headers) { Cell cell = headerRow.createCell(colIndex++); cell.setCellValue(header.getTitle()); // 设置合并单元格 if (header.getColSpan() > 1 || header.getRowSpan() > 1) { CellRangeAddress rangeAddress = new CellRangeAddress(0, header.getRowSpan() - 1, colIndex - 1, colIndex + header.getColSpan() - 2); sheet.addMergedRegion(rangeAddress); } } // 填充数据 int rowIndex = 1; for (Map<String, Object> data : dataList) { Row row = sheet.createRow(rowIndex++); colIndex = 0; for (ExcelHeader header : headers) { // 获取数据 Object value = data.get(header.getFieldName()); // 格式化日期 if (value instanceof Date) { value = new SimpleDateFormat(dateFormat).format(value); } Cell cell = row.createCell(colIndex++); cell.setCellValue(String.valueOf(value)); } } // 设置响应头 response.setContentType("application/octet-stream;charset=utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + new String((title + ".xlsx").getBytes("utf-8"), "iso8859-1")); // 输出Excel文件 OutputStream outputStream = response.getOutputStream(); workbook.write(outputStream); outputStream.flush(); outputStream.close(); } } ``` 其中,ExcelHeader类表示表头信息,包括标题、字段名、合并数、行合并数等信息: ```java public class ExcelHeader { /** * 标题 */ private String title; /** * 字段名 */ private String fieldName; /** * 合并数 */ private int colSpan; /** * 行合并数 */ private int rowSpan; public ExcelHeader(String title, String fieldName, int colSpan, int rowSpan) { this.title = title; this.fieldName = fieldName; this.colSpan = colSpan; this.rowSpan = rowSpan; } // getter/setter方法省略 } ``` 使用示例: ```java List<ExcelHeader> headers = new ArrayList<>(); headers.add(new ExcelHeader("姓名", "name", 1, 1)); headers.add(new ExcelHeader("年龄", "age", 1, 1)); headers.add(new ExcelHeader("联系方式", null, 2, 1)); headers.add(new ExcelHeader("电话", "phone", 1, 1)); headers.add(new ExcelHeader("邮箱", "email", 1, 1)); headers.add(new ExcelHeader("地址", "address", 1, 1)); headers.add(new ExcelHeader("省份", "province", 1, 1)); headers.add(new ExcelHeader("城市", "city", 1, 1)); headers.add(new ExcelHeader("区县", "county", 1, 1)); List<Map<String, Object>> dataList = new ArrayList<>(); Map<String, Object> data1 = new HashMap<>(); data1.put("name", "张三"); data1.put("age", 20); data1.put("phone", "123456789"); data1.put("email", "zhangsan@example.com"); data1.put("address", "北京市朝阳区"); data1.put("province", "北京市"); data1.put("city", "北京市"); data1.put("county", "朝阳区"); dataList.add(data1); CustomExportUtil.exportExcel("学生信息表", headers, dataList, "yyyy-MM-dd HH:mm:ss", response); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值