java动态导出Excle模板

实际开发中很多时候都需要导出Excle模板文件,而且是根据不同的需求需要导出不同的模板,在这里简单记录一下使用java实现普通的动态Excle模板导出功能。

1.添加maven依赖:

		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.8</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.8</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml-schemas</artifactId>
			<version>3.8</version>
		</dependency>
  1. 编写导出工具类:
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFDataValidationConstraint;
import org.apache.poi.xssf.usermodel.XSSFDataValidationHelper;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class ExcelDownloadUtil {

    /**
     * 标题单元格默认长度
     */
    private static final int DEFAULT_COLUMN_SIZE = 20;

    /**
     * EXCEL含有下拉列表默认受影响行数
     */
    private static final int SELECT_HEIGHT = 300;

    /**
     * 用于动态生成导出模板
     * 
     * @param response 用于下载
     * @param sheetName 工作表空间名称
     * @param columnNames
     * @param title Excle模板标题,不需要则传null
     * @throws Exception
     */
    public static void getExcelTitleTemplate(HttpServletResponse response, String sheetName,
        List<Map<String, Object>> columnNames, String title) throws Exception {
        Workbook workBook = new XSSFWorkbook();
        // 生成一个表格
        Sheet sheet = workBook.getSheet(sheetName);
        if (sheet == null) {
            sheet = workBook.createSheet(sheetName);
        }
        // 最新Excel列索引,从0开始
        int lastRowIndex = sheet.getLastRowNum();
        if (lastRowIndex > 0) {
            lastRowIndex++;// 如果已经存在了工作表空间则从下一行开始
        }
        if (!StringUtils.isBlank(title)) {
            // 如果需要合并单元格显示一个大的title
            Row titleRow = sheet.createRow(lastRowIndex);
            //参数说明:1.起始行号 2.终止行号 3.起始列号 4.终止列号
            //这个需要实际情况自定义,我这里测试就写死0 0 0 4 了
            CellRangeAddress region = new CellRangeAddress(0, 0, 0, 4);
            sheet.addMergedRegion(region);// 合并单元格
            Cell cellTiltle = titleRow.createCell(0);
            // 设置样式
            cellTiltle.setCellStyle(createCellHeadStyle(workBook, false));
            // 单元格设置值
            cellTiltle.setCellValue(title);
            lastRowIndex++;
        }

        // 设置表格默认列宽度
        sheet.setDefaultColumnWidth(DEFAULT_COLUMN_SIZE);
        // 产生表格表头列标题行
        Row row = sheet.createRow(lastRowIndex);
        for (int i = 0; i < columnNames.size(); i++) {
            Map<String, Object> map = columnNames.get(i);
            Cell cell = row.createCell(i);
            // 设置单元格类型
            cell.setCellType(HSSFCell.CELL_TYPE_STRING);
            // 设置单元格样式
            cell.setCellStyle(createCellHeadStyle(workBook, (boolean)map.get("isSpecial")));
            // 填充表头文本
            RichTextString text = new XSSFRichTextString((String)map.get("columnname"));
            cell.setCellValue(text);
            String[] selectValue = (String[])map.get("selectValue");
            // 如果需要有下拉列表则显示
            if (selectValue != null && selectValue.length != 0) {
                // 准备下拉列表数据
                CellRangeAddressList regions = new CellRangeAddressList(1, SELECT_HEIGHT, i, i);
                // 创建下拉列表数据
                XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet);
                XSSFDataValidationConstraint dvConstraint =
                    (XSSFDataValidationConstraint)dvHelper.createExplicitListConstraint(selectValue);
                XSSFDataValidation validation = (XSSFDataValidation)dvHelper.createValidation(dvConstraint, regions);
                validation.setSuppressDropDownArrow(true);
                validation.setShowErrorBox(true);
                sheet.addValidationData(validation);
            }
        }
        OutputStream outputStream = response.getOutputStream();
        workBook.write(outputStream);// HSSFWorkbook写入流,下载
        outputStream.flush();// 刷新流
        outputStream.close();// 关闭流

    }

    /**
     * 创建单元格表头样式
     *
     * @param workbook 工作薄
     * @param bgColorYellow true-背景使用黄色
     * @return
     */
    private static CellStyle createCellHeadStyle(Workbook workbook, boolean bgColorYellow) {
        CellStyle style = workbook.createCellStyle();
        // 设置边框样式
        style.setBorderBottom(XSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(XSSFCellStyle.BORDER_THIN);
        style.setBorderRight(XSSFCellStyle.BORDER_THIN);
        style.setBorderTop(XSSFCellStyle.BORDER_THIN);
        // 设置对齐样式
        style.setAlignment(XSSFCellStyle.ALIGN_CENTER);
        // 生成字体
        Font font = workbook.createFont();
        // 表头样式
        style.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);
        // 如果是true,则显示黄色背景色
        style.setFillForegroundColor(bgColorYellow ? HSSFColor.YELLOW.index : HSSFColor.GREY_25_PERCENT.index);
        font.setFontHeightInPoints((short)12);
        font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样式
        style.setFont(font);
        return style;
    }

    /**
     * 得到有效的Excel文件列有效数据
     * 
     * @param xianShiList
     * @param biTianList
     * @param jiGouId
     * @return
     */
    public static List<Map<String, Object>> getColumnNames(List<String> xianShiList, List<String> biTianList,
        String condition) {
        List<Map<String, Object>> columnNames = new ArrayList<Map<String, Object>>();
        Map<String, Object> resultMap = null;
        List<String> selectValues = null;
        boolean validaBiTianList = biTianList != null && !biTianList.isEmpty();
        for (int i = 0, len = xianShiList.size(); i < len; i++) {
            resultMap = new HashMap<String, Object>();
            // 得到下载的字段名称
            String columnName = xianShiList.get(i);
            // 判断该字段是否是字典类型,如果是字典类型的字段则需要显示下拉列表
            // 我这里用了一个枚举类涵盖所有的可能涉及的下载字段,每次判断一下是否需要获取字典值
            FieldEnum field = FieldEnum.getFieldEnum(columnName);
            if (field != null) {
                resultMap.put("columnname", columnName);
                // 判断字段是否是必填字段
                if (validaBiTianList && biTianList.contains(columnName)) {
                    resultMap.put("isSpecial", true);// 如果是必填则标识为true
                } else {
                    resultMap.put("isSpecial", false);
                }
                if (field.isZiDian()) {// 如果是字典值
                    // 获取字典内容
                    // ZiDianEntity ziDian = ZiDianUtil.getZiDian(columnName, condition);
                    // List<ZiDianZhiEntity> ziDianZhiEntityList = ziDian.getZiDianZhiList();
                    selectValues = new ArrayList<>();
                    // 添加字典值到下拉列表
                    /*for (int j = 0, lenx = ziDianZhiEntityList.size(); j < lenx; j++) {
                        selectValues.add(ziDianZhiEntityList.get(j).getZiDianZhi());
                    }*/
                    // 这里为了测试直接写死了
                    selectValues.add("男");
                    selectValues.add("女");
                    selectValues.add("未知");
                    resultMap.put("selectValue", selectValues.toArray(new String[selectValues.size()]));
                }
                columnNames.add(resultMap);
            }
        }

        return columnNames;
    }

    /**
     * 得到EXCEL模板下载时的显示字段
     * 
     * @param condition
     * @return
     * @throws Exception
     */
    public static List<String> listXianShiField(String condition) throws Exception {
        List<String> resultList = listExcelField("EXCEL下载时显示字段", condition);
        resultList = resultList != null && resultList.size() != 0 ? resultList
            : Arrays.asList(new String[] {"姓名", "身份证号", "性别", "联系电话", "联系地址"});
        return resultList;
    }

    /**
     * 得到EXCEL模板下载时的必填字段
     * 
     * @param jiGouId
     * @return
     * @throws Exception
     */
    public static List<String> listBiTianField(String jiGouId) throws Exception {
        List<String> resultList = listExcelField("EXCEL必填字段", jiGouId);
        resultList =
            resultList != null && resultList.size() != 0 ? resultList : Arrays.asList(new String[] {"姓名", "性别"});
        return resultList;
    }

    /**
     * 通过设置名称获取显示list集合
     * 
     * @param sheZhiMingCheng
     * @param condition
     * @return
     * @throws Exception
     */
    public static List<String> listExcelField(String sheZhiMingCheng, String condition) throws Exception {
        List<String> list = null;
        // 可以从数据库中动态获取,或者从程序中动态拼接出想要导出的字段
        //do something
        return list;
    }
}

枚举类补充:

public enum FieldEnum {
    /**
     * 姓名
     */
    XING_MING("姓名", false),
    /**
     * 身份证号
     */
    SHEN_FEN_ZHENG_HAO("身份证号", false),

    /**
     * 性别
     */
    XING_BIE("性别", true),

    /**
     * 婚姻状况
     */
    HUN_YING_ZHUANG_KUANG("婚姻状况", true),

    /**
     * 联系人
     */
    LIAN_XI_REN("联系人", false),
    /**
     * 联系电话
     */
    LIAN_XI_DIAN_HUA("联系电话", false),
    /**
     * 联系地址
     */
    LIAN_XI_DI_ZHI("联系地址", false);

    /* 省略其他可能的属性 */

    private String name;

    private boolean isZiDian;

    private FieldEnum(String name, boolean isZiDian) {
        this.name = name;
        this.isZiDian = isZiDian;
    }

    /**
     * 通过name获取枚举类
     * 
     * @param name
     * @return
     */
    public static FieldEnum getFieldEnum(String name) {
        FieldEnum[] values = FieldEnum.values();
        for (int i = 0; i < values.length; i++) {
            if (values[i].getName().equals(name)) {
                return values[i];
            }
        }
        return null;
    }

    public String getName() {
        return name;
    }

    public boolean isZiDian() {
        return isZiDian;
    }

}

3.实现下载:

/**
     * 下载动态模板
     * 
     * @param request
     * @param response
     */
    @RequestMapping(value = "/downloadDongTaiMuBan.do")
    public void downloadDongTaiMuBan(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 这里只是模仿一种条件,真正的条件需要根据实际情况传入
            String condition = request.getRequestURL().toString();
            System.out.println(condition);
            // 根据条件从数据库动态获取需要导出的EXCEL字段
            List<String> xianShiList = ExcelDownloadUtil.listXianShiField(condition);
            if (xianShiList != null && !xianShiList.isEmpty()) {
                // 根据条件从数据库动态获取必填的EXCEL字段
                List<String> biTianList = ExcelDownloadUtil.listBiTianField(condition);
                // 文件名编码,解决乱码问题
                String fileName = "登记.xlsx";
                String userAgentString = request.getHeader("User-Agent");
                String browser = UserAgent.parseUserAgentString(userAgentString).getBrowser().getGroup().getName();
                if (browser.equals("Chrome") || browser.equals("Internet Exploer") || browser.equals("Safari")) {
                    fileName = URLEncoder.encode(fileName, "utf-8").replaceAll("\+", "%20");
                } else {
                    fileName = MimeUtility.decodeText(fileName);
                }
                // 设置请求
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/x-download");
                response.addHeader("Content-Disposition", String.format("attachment; filename="%s"", fileName));
                response.addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
                response.addHeader("Pragma", "no-cache");
                response.addHeader("Expires", "0");

                final String sheetName = "第一分组";// 设置EXCEL默认工作表空间名称
                // 处理EXCEL字段,标识出需要必填的字段,有下拉列表的字段,获取下拉列表的值
                List<Map<String, Object>> columnNames =
                    ExcelDownloadUtil.getColumnNames(xianShiList, biTianList, condition);
                if (columnNames != null && !columnNames.isEmpty()) {
                    ExcelDownloadUtil.getExcelTitleTemplate(response, sheetName, columnNames, "登记信息");
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("生成下载动态模板异常!", e);
        }

    }

4.效果预览:
在这里插入图片描述
通过以上代码就简单实现Excle模板动态导出功能啦。

一颗安安静静的小韭菜。文中如果有什么错误,欢迎指出。
原文地址:https://www.cnblogs.com/c-Ajing/p/13448381.html