阿里开源(EasyExcel):使用Java将数据导出为Excel表格、带样式----》java web下载 Excel文件

一、技术选型

ava解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便

easyExcel官方GitHub地址:内有详细的讲解

小编最近在工作中刚好遇到了这么一个需求,将一系列数据导出为Excel表格。然后就开始百度,就看到了上面这段话。 经过小编对 poi 和 easyExcel 的demo的对比,决定使用easyExcel实现这个需求。

二、实现过程

1、导入依赖

建议去GitHub查看最新版本号:

         
        
            com.alibaba
            easyexcel
            1.1.2-beta5
        

2、编写工具类 EasyExcelUtil

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Font;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.TableStyle;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.hanclouds.teamwork.entity.EasyExcelParams;
import org.apache.poi.ss.usermodel.IndexedColors;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author MouFangCai
 * @date 2019/7/23 11:48
 */
public class EasyExcelUtil {

    /**
     * 下载EXCEL文件2007版本
     *
     * @throws IOException IO异常
     */
    public static void exportExcel2007Format(EasyExcelParams excelParams) throws IOException {
        exportExcel(excelParams, ExcelTypeEnum.XLSX);
    }

    /**
     * 下载EXCEL文件2003版本
     *
     * @throws IOException IO异常
     */
    public static void exportExcel2003Format(EasyExcelParams excelParams) throws IOException {
        exportExcel(excelParams, ExcelTypeEnum.XLS);
    }

    /**
     * 根据参数和版本枚举导出excel文件
     *
     * @param excelParams 参数实体
     * @param typeEnum    excel类型枚举
     * @throws IOException
     */
    private static void exportExcel(EasyExcelParams excelParams, ExcelTypeEnum typeEnum) throws IOException {
        HttpServletResponse response = excelParams.getResponse();
        ServletOutputStream out = response.getOutputStream();

        // ExcelWriter提供了多种构造方式,可自行查看选择所需要的
        ExcelWriter writer = new ExcelWriter(null, out, typeEnum,
                true, excelParams.getWriteHandler());

        // 设置web下载等的信息
        prepareResponds(response, typeEnum);
        // 创建一个sheet
        Sheet sheet = new Sheet(1, 0, excelParams.getDataModelClazz());
        sheet.setSheetName(excelParams.getSheetName());

        // 设置列宽 设置每列的宽度
        Map columnWidth = new HashMap<>(6);
        columnWidth.put(0,6666);
        columnWidth.put(1,5000);
        columnWidth.put(2,15000);
        columnWidth.put(3,3000);
        columnWidth.put(4,20000);
        columnWidth.put(5,10000);
        sheet.setColumnWidthMap(columnWidth);
        sheet.setAutoWidth(Boolean.TRUE);
        // 用于设置 表格样式
        sheet.setTableStyle(createTableStyle());
        // 写入Excel中,也提供了多个重载方法,根据需要选择
        writer.write(excelParams.getData(), sheet);

        // 根据数据展示需要,用于合并单元格
        List mergeList = excelParams.getMergeList();
        if (mergeList != null && mergeList.size() > 0){
            for (int[] arr : mergeList) {
                // 待合并的单元格参数:开始的行数,结束的行数,开始的列数,结束的列数
                writer.merge(arr[0], arr[1], arr[2], arr[3]);
            }
        }
        writer.finish();
        out.flush();
    }

    /**
     * 将文件输出到浏览器(导出文件)
     *
     * @param response 响应
     * @param typeEnum excel类型
     */
    private static void prepareResponds(HttpServletResponse response,ExcelTypeEnum typeEnum) throws UnsupportedEncodingException {

        String fileName = new String((new SimpleDateFormat("MMddHHmm").format(new Date()))
                .getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        response.setContentType("multipart/form-data");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + typeEnum.getValue());
    }

    /**
     * 表格样式:目前easyExcel的样式调整,仅支持表头和内容两部分别统一设置
     * @return
     */
    private static TableStyle createTableStyle(){
        TableStyle tableStyle = new TableStyle();
        tableStyle.setTableHeadBackGroundColor(IndexedColors.GREY_50_PERCENT);
        tableStyle.setTableContentBackGroundColor(IndexedColors.GREY_25_PERCENT);
        Font contentFont = new Font();
        contentFont.setFontName("黑体");
        contentFont.setFontHeightInPoints((short)12);
        tableStyle.setTableContentFont(contentFont);
        Font headFont = new Font();
        headFont.setFontName("黑体");
        headFont.setFontHeightInPoints((short)10);
        tableStyle.setTableHeadFont(headFont);
        return tableStyle;
    }

}

writer.merge(arr[0], arr[1], arr[2], arr[3]); 关于合并单元的这个merge方法

public ExcelWriter merge(int firstRow, int lastRow, int firstCol, int lastCol) {
    this.excelBuilder.merge(firstRow, lastRow, firstCol, lastCol);
    return this;
}

通过查看方法详情,即可理解:开始合并的行数,结束合并的行数,开始合并的列数,结束合并的列数

需要注意的是:对应的 last 的值必须大于等于 first 的值

3、公用参数类 EasyExcelParams

import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Table;
import com.hanclouds.teamwork.util.MyWriteHandler;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * @author MouFangCai
 * @date 2019/7/23 20:18
 */
public class EasyExcelParams {

    /**
     * 表格样式:使用
     */
    private MyWriteHandler writeHandler;
    /**
     * 表格样式:未使用
     */
    private Table table;
    /**
     * 需合并的单元格参数配置集合 list
     */
    private List mergeList;
    /**
     * excel文件名(不带拓展名)
     */
    private String excelNameWithoutExt;
    /**
     * sheet名称
     */
    private String sheetName;
    /**
     * 是否需要表头
     */
    private boolean needHead = true;
    /**
     * 数据
     */
    private List data;

    /**
     * 数据模型类型
     */
    private Class dataModelClazz;

    /**
     * 响应
     */
    private HttpServletResponse response;

    public EasyExcelParams() {
    }

    public MyWriteHandler getWriteHandler() {
        return writeHandler;
    }

    public void setWriteHandler(MyWriteHandler writeHandler) {
        this.writeHandler = writeHandler;
    }

    public Table getTable() {
        return table;
    }

    public void setTable(Table table) {
        this.table = table;
    }

    public List getMergeList() {
        return mergeList;
    }

    public void setMergeList(List mergeList) {
        this.mergeList = mergeList;
    }

    public String getExcelNameWithoutExt() {
        return excelNameWithoutExt;
    }

    public void setExcelNameWithoutExt(String excelNameWithoutExt) {
        this.excelNameWithoutExt = excelNameWithoutExt;
    }

    public String getSheetName() {
        return sheetName;
    }

    public void setSheetName(String sheetName) {
        this.sheetName = sheetName;
    }

    public boolean isNeedHead() {
        return needHead;
    }

    public void setNeedHead(boolean needHead) {
        this.needHead = needHead;
    }

    public List getData() {
        return data;
    }

    public void setData(List data) {
        this.data = data;
    }

    public Class getDataModelClazz() {
        return dataModelClazz;
    }

    public void setDataModelClazz(Class dataModelClazz) {
        this.dataModelClazz = dataModelClazz;
    }

    public HttpServletResponse getResponse() {
        return response;
    }

    public void setResponse(HttpServletResponse response) {
        this.response = response;
    }
}

4、表格样式实体类 MyWriteHandler

关于使用easyExcel去进行表格样式的设置,个人感觉对于复杂的样式成本是比较的,所以小编这里的样式,仅仅只有边框、水平居中、垂直居中、内容自适应。而且整个表格内容的格式是一样的。

package com.hanclouds.teamwork.util;

import com.alibaba.excel.event.WriteHandler;
import org.apache.poi.ss.usermodel.*;

/**
 * @author MouFangCai
 * @date 2019/7/28 18:06
 */
public class MyWriteHandler implements WriteHandler {
    private CellStyle cellStyle;
    @Override
    public void sheet(int i, Sheet sheet) {
        Workbook workbook = sheet.getWorkbook();
        // 创建样式
        cellStyle = workbook.createCellStyle();
    }

    @Override
    public void row(int i, Row row) {
    }

    @Override
    public void cell(int i, Cell cell) {
        if (cell.getRowIndex() >0) {
            createStyle();
            cell.setCellStyle(this.cellStyle);
        }
        }

    private void createStyle() {
          // 填充色:和EasyExcelUtil里的createTableStyle效果一样
//        cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
//        cellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.BLUE.getIndex());
//        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        // 下边框
        cellStyle.setBorderBottom(BorderStyle.THIN);
        // 左边框
        cellStyle.setBorderLeft(BorderStyle.THIN);
        // 右边框
        cellStyle.setBorderRight(BorderStyle.THIN);
        // 上边框
        cellStyle.setBorderTop(BorderStyle.THIN);
        // 设置自动换行
        cellStyle.setWrapText(true);
        // 水平对齐方式
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setFillBackgroundColor((short)22);
        // 垂直对齐方式
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
    }

}

5、数据实体类(也是表头)BaseRowModel

注意,必须继承** BaseRowModel**

** @ExcelProperty(value = "部门",index = 0) 该注解的作用就是:声明表头名,和对应的位置,index=0表示在列数“第一列”**

package com.hanclouds.teamwork.entity;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;

/**
 * @author MouFangCai
 * @date 2019/7/24 9:18
 */

public class WeeklyExportDto extends BaseRowModel {

    @ExcelProperty(value = "部门",index = 0)
    private String groupName;

    @ExcelProperty(value = "项目名称",index = 1)
    private String projectName;

    @ExcelProperty(value = "任务名",index = 2)
    private String weeklyTaskName;

    @ExcelProperty(value = "责任人",index = 3)
    private String dutyUserName;

    @ExcelProperty(value = "完成情况、下周计划",index = 4)
    private String detail;

    @ExcelProperty(value = "任务描述",index = 5)
    private String taskDescription;

    public WeeklyExportDto() {
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public String getWeeklyTaskName() {
        return weeklyTaskName;
    }

    public void setWeeklyTaskName(String weeklyTaskName) {
        this.weeklyTaskName = weeklyTaskName;
    }

    public String getDutyUserName() {
        return dutyUserName;
    }

    public void setDutyUserName(String dutyUserName) {
        this.dutyUserName = dutyUserName;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public String getTaskDescription() {
        return taskDescription;
    }

    public void setTaskDescription(String taskDescription) {
        this.taskDescription = taskDescription;
    }
}

6、主程序

数据结构说明:

参考:8、导出的Excel效果

待导出的数据结构如下:
---有多个分组:List
----一个分组下,有多个项目: List weeklyProjectList = weeklyGroupRes.getWeeklyProjectList();
----一个项目下,有多个任务: List itemDtoList = weeklyProjectDto.getItemDtoList();

同一个分组,需合并单元格;同一个项目,需合并单元格。

 @GetMapping("/export")
 public boolean exportExcel( HttpServletResponse response) {

        // 先从数据库 获取 需要导出的数据(此处自查)
         List dataList = new ArrayList<>();
        // mergeList 用于存放合并单元格的配置信息
        // int[] 数据存放4个参数,分别对应merge方法的参数
        // (int firstRow, int lastRow, int firstCol, int lastCol)
        List mergeLis =new ArrayList<>();
        // 用于存放 写入Excel的数据
        List resultList = new ArrayList<>();
        // 用于生成merge
        int lastRow = 0;
        // 遍历处理需要导出的数据,下面就是小编对数据结构的遍历处理,可以忽略
        for (WeeklyGroupRes weeklyGroupRes : dataList) {
            int[] groupInt = new int[4];
            groupInt[0] = lastRow + 1;
            List weeklyProjectList = weeklyGroupRes.getWeeklyProjectList();

            for (WeeklyProjectDto weeklyProjectDto : weeklyProjectList) {
                int[] projectInt = new int[4];
                projectInt[0] = lastRow + 1;
                List itemDtoList = weeklyProjectDto.getItemDtoList();

                for (WeeklyItemDto weeklyItemDto : itemDtoList) {
                    String detail = thisComplete + weeklyItemDto.getComplete()
                            + "\n" + nextPlan + weeklyItemDto.getPlan();
                    Task task = taskMap.get(weeklyItemDto.getTaskId());
                    WeeklyExportDto exportDto = new WeeklyExportDto();
                    exportDto.setGroupName(weeklyGroupRes.getMemberGroupName());
                    exportDto.setProjectName(weeklyProjectDto.getProjectName());
                    exportDto.setWeeklyTaskName(weeklyItemDto.getWeeklyTaskName());
                    exportDto.setDutyUserName(weeklyItemDto.getUserNickName());
                    exportDto.setDetail(detail);
                    exportDto.setTaskDescription(task.getDescription());
                    resultList.add(exportDto);
                }
                lastRow = lastRow + itemDtoList.size();
                projectInt[1] = lastRow;
                projectInt[2] = 1;
                projectInt[3] = 1;
                if (projectInt[0] != projectInt[1]) {
                    mergeLis.add(projectInt);
                }
            }
            groupInt[1] = lastRow;
            groupInt[2] = 0;
            groupInt[3] = 0;
            if (groupInt[0] != groupInt[1]) {
                mergeLis.add(groupInt);
            }
        }
        // 设置各种参数,用于导出Excel
        EasyExcelParams easyExcelParams = new EasyExcelParams();
        easyExcelParams.setSheetName("导出Excel");
        easyExcelParams.setResponse(response);
        easyExcelParams.setData(resultList);
        easyExcelParams.setDataModelClazz(WeeklyExportDto.class);
        easyExcelParams.setNeedHead(false);
        easyExcelParams.setMergeList(mergeLis);
        easyExcelParams.setWriteHandler(new MyWriteHandler());
        try {
            EasyExcelUtil.exportExcel2007Format(easyExcelParams);
        } catch (IOException e) {
            e.printStackTrace();
            throw new HanCloudsException(CommonErrorCode.INTERNAL_SERVER_ERROR.getErrorCode(),
                    "Export failed. Please try again");
        }
        return true;
    }

7、运行程序,效果如下:

阿里开源(EasyExcel):使用Java将数据导出为Excel表格、带样式----》java web下载 Excel文件_第1张图片

8、导出的Excel效果

阿里开源(EasyExcel):使用Java将数据导出为Excel表格、带样式----》java web下载 Excel文件_第2张图片

你可能感兴趣的:(阿里开源(EasyExcel):使用Java将数据导出为Excel表格、带样式----》java web下载 Excel文件)