Java Poi 导出功能优化

1.上一篇关于Java POI导出的文章,导出功能没有达到预期目标,有很多地方需要优化,比如,读取模板。Java Poi 导出功能优化_第1张图片使用上面的方式,不能获取资源文件,可使用以下方式获取。
InputStream is = this.getClass().getClassLoader().getResourceAsStream("template/" + templateName);
总的来说,抛弃了很多东西。不再使用excel模板进行导出。全过程使用代码填充。包括生成表头,聚合表头,填充数据,跨行跨列。代码下载链接,不收费https://download.csdn.net/download/ly9918/12346513

2.导出功能实现效果Java Poi 导出功能优化_第2张图片

3.下面是用到的辅助类截图
Java Poi 导出功能优化_第3张图片

public class ExcelField {

    private Integer level;

    private String fieldName;

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }
}
import java.util.Collection;
import java.util.List;

public class ExcelHeader {

    private String index;

    private String name;

    private List children;

    public String getIndex() {
        return index;
    }

    public void setIndex(String index) {
        this.index = index;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Collection getChildren() {
        return children;
    }

    public void setChildren(List children) {
        this.children = children;
    }
}
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;

public class ExcelProfile {

    private String excelName;

    private Collection data;

    private List excelHeaders;

    private Integer headerLevel;

    private List excelFields;

    private Integer fieldLevel;

    private LinkedHashMap specialKeyMap;

    public String getExcelName() {
        return excelName;
    }

    public void setExcelName(String excelName) {
        this.excelName = excelName;
    }

    public Collection getData() {
        return data;
    }

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

    public List getExcelHeaders() {
        return excelHeaders;
    }

    public void setExcelHeaders(List excelHeaders) {
        this.excelHeaders = excelHeaders;
    }

    public Integer getHeaderLevel() {
        return headerLevel;
    }

    public void setHeaderLevel(Integer headerLevel) {
        this.headerLevel = headerLevel;
    }

    public List getExcelFields() {
        return excelFields;
    }

    public void setExcelFields(List excelFields) {
        this.excelFields = excelFields;
    }

    public Integer getFieldLevel() {
        return fieldLevel;
    }

    public void setFieldLevel(Integer fieldLevel) {
        this.fieldLevel = fieldLevel;
    }

    public LinkedHashMap getSpecialKeyMap() {
        return specialKeyMap;
    }

    public void setSpecialKeyMap(LinkedHashMap specialKeyMap) {
        this.specialKeyMap = specialKeyMap;
    }
}
import java.util.Collection;
import java.util.HashMap;

public class MultilevelData {

    private HashMap properties;

    private Collection children;

    public HashMap getProperties() {
        return properties;
    }

    public void setProperties(HashMap properties) {
        this.properties = properties;
    }

    public Collection getChildren() {
        return children;
    }

    public void setChildren(Collection children) {
        this.children = children;
    }
}

3.主要用到的工具类

package com.demo.am.util;

import com.demo.am.entity.excel.ExcelField;
import com.demo.am.entity.excel.ExcelHeader;
import com.demo.am.entity.excel.ExcelProfile;
import com.demo.am.entity.excel.MultilevelData;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

public class ExcelExportUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportUtil.class);

    // 默认排除的key
    private static final String EXCLUDE_KEY = "id";

    // 默认EXCEL后缀
    private static final String DEFAULT_SUFFIX = ".xlsx";

    // 默认编码
    private static final String DEFAULT_ENCODING = "UTF-8";

    // 默认时间格式
    private static final String DEFAULT_DATE_PATTEN = "yyyy/MM/dd";

    // 默认Row access window size
    private static final Integer DATA_SIZE = 100 * 10;

    // 默认字体
    private static final String DEFAULT_FONT_NAME = HSSFFont.FONT_ARIAL;

    // 默认EXCEL NAME所在行
    private static final Integer EXCEL_NAME_ROW = 0;

    // 默认起始列
    private static final Integer BEGIN_COLUMN = 0;

    // 默认EXCEL NAME所在行行高
    private static final Integer EXCEL_NAME_ROW_HEIGHT = 20;

    // 默认行高
    private static final Integer DEFAULT_ROW_HEIGHT = 10;

    // 最大列宽长度
    private static final Integer MAX_COLUMN_WIDTH = 10;

    // 填充表头完成行数
    private static Integer END_HEADER_ROW = 0;

    // EXCEL column number
    private static Integer COLUMN_NUMBER = 0;

    // 列宽集合
    private static List WIDTH_DATA = new ArrayList<>();

    public static void exportExcel(ExcelProfile excelProfile, HttpServletResponse response) {
        exportExcel(Collections.singletonList(excelProfile), response);
    }

    private static void exportExcel(List excelProfileList, HttpServletResponse response) {
        List errors = new ArrayList<>();
        // 可以更换 XSSFWorkbook wb = new XSSFWorkbook(); 或 HSSFWorkbook wb = new HSSFWorkbook();
        // 区别在于 前者为xlsx格式 后者为xls格式 , SXSSFWorkbook存在默认值100超过一百行写入磁盘文件,所以最后需要清理临时文件
        XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
        SXSSFWorkbook wb = new SXSSFWorkbook(xssfWorkbook, DATA_SIZE, Boolean.TRUE);
        if (CollectionUtils.isNotEmpty(excelProfileList)) {
            // 设置单元格样式
            CellStyle style = setDefaultStyle(wb);
            for (int i = 0; i < excelProfileList.size(); i++) {
                ExcelProfile excelProfile = excelProfileList.get(i);
                if (excelProfile != null) {
                    excelProfile.setExcelName(processExcelName(excelProfile.getExcelName()));
                    // 创建一个工作表
                    Sheet sheet = wb.createSheet("sheet" + i);
                    // 缩放比例70%
                    sheet.setZoom(70);
                    try {
                        fillExcelSheet(excelProfile.getExcelName(),
                                sheet, style,
                                excelProfile.getData(),
                                excelProfile.getExcelHeaders(),
                                excelProfile.getExcelFields(),
                                excelProfile.getSpecialKeyMap());
                    } catch (Exception e) {
                        // 捕获异常,中断循环
                        errors.add(e.getMessage());
                        break;
                    }
                } else {
                    errors.add("导出失败!");
                    break;
                }
            }
        } else {
            errors.add("导出失败!");
        }

        // 导出失败存在以下两种可能
        // 1.excelProfile为空
        // 2.EXCEL表头没有填充
        if (CollectionUtils.isNotEmpty(errors)) {
            // 清理临时文件
            wb.dispose();

            // 关闭工作簿
            closeWorkBook(wb);

            // 提示消息去重复 (可能会出现重复)
            List results = errors.stream().distinct().collect(Collectors.toList());
            results.forEach(System.out::println);
        } else {
            // 处理响应头
            processExcelResponse(response);
            // 输出响应流
            outputResponseStream(response, wb);
            // 输出文件
            // outputFileStream(wb);
            // 清理临时文件
            wb.dispose();
            // 关闭工作簿
            closeWorkBook(wb);
        }
    }

    /**
     * fill excel sheet.
     *
     * @param excelName     excel name
     * @param sheet         excel sheet
     * @param style         cell style
     * @param data          List/List<实体类> example :List
     * @param headers       List/List
     * @param excelFields   excelFields
     * @param specialKeyMap specialKeyMap
     */
    private static void fillExcelSheet(String excelName, Sheet sheet, CellStyle style,
                                       Collection data, Collection headers, List excelFields,
                                       LinkedHashMap specialKeyMap) throws Exception {
        // excel名称所在行 第一行
        Row excelNameRow = sheet.createRow(EXCEL_NAME_ROW);
        int beginRow = EXCEL_NAME_ROW + 1;
        int rowNumber = beginRow;
        int columnNumber = BEGIN_COLUMN;
        List list = new ArrayList<>();

        // 生成表头
        columnNumber = fillExcelHeader(sheet, style, rowNumber, columnNumber, headers, list);
        // 表头不填充,无任何响应
        if (columnNumber > 0) {
            // 填充表头以后,列数正确,但所占行数不正确,故使用list存放行数,获取最大值
            rowNumber = Collections.max(list);
            // 填充完成表头所得到row number
            END_HEADER_ROW = rowNumber;
            COLUMN_NUMBER = columnNumber;
            // 处理excel名称所在行
            processExcelNameRow(sheet, excelNameRow, style, columnNumber, excelName);
            // 处理表头,包括合并、样式等等
            processHeaderRow(sheet, beginRow, rowNumber, columnNumber);
            // 处理表头的特殊值
            processSpecialKey(sheet, specialKeyMap, rowNumber, columnNumber);
            // 填充excel
            fillExcelData(sheet, style, rowNumber, columnNumber, data, excelFields);
            // 如果使用自动列宽需要注释掉所有 calculateWidth(),calculateHeaderColumnWidth(),setColumnWidth()
            // setAutoColumnWidth(sheet,style);
        } else {
            throw new Exception("导出失败!");
        }
    }

    /**
     * Fill excel header.
     *
     * @param excelHeaders header
     * @param rowNumber    row
     * @param columnNumber column
     * @param sheet        sheet
     * @param style        style
     * @param indexList    list
     * @return column
     */
    private static Integer fillExcelHeader(Sheet sheet, CellStyle style,
                                           Integer rowNumber, Integer columnNumber,
                                           Collection excelHeaders,
                                           List indexList) {
        Row row;
        indexList.add(rowNumber);
        if (sheet.getRow(rowNumber) != null) {
            row = sheet.getRow(rowNumber);
        } else {
            row = sheet.createRow(rowNumber);
        }

        int beginRow = rowNumber;
        int beginColumn = columnNumber;
        int endRow;
        if (CollectionUtils.isNotEmpty(excelHeaders)) {
            if (excelHeaders.toArray()[0] instanceof ExcelHeader) {
                List headers = convertExcelHeaderList(excelHeaders);
                for (ExcelHeader header : headers) {
                    fillHeaderData(row, style, Collections.singletonList(header.getName()), columnNumber);
                    if (CollectionUtils.isNotEmpty(header.getChildren())) {
                        columnNumber = fillExcelHeader(sheet, style,
                                rowNumber + 1, columnNumber, header.getChildren(), indexList);
                    } else {
                        columnNumber++;
                    }
                }
            } else {
                List list = new ArrayList<>();
                if (excelHeaders.toArray()[0] instanceof String) {
                    for (Object object : excelHeaders) {
                        list.add(String.valueOf(object));
                    }
                }
                fillHeaderData(row, style, list, columnNumber);
            }
            rowNumber++;
        }

        // 合并列
        endRow = rowNumber;

        if (endRow - beginRow == 1 && beginRow != 0) {
            Row hssfRow = sheet.getRow(beginRow - 1);
            if (columnNumber - beginColumn > 1 && hssfRow.getCell(beginColumn) != null &&
                    hssfRow.getCell(beginColumn + 1) == null) {
                CellRangeAddress region = new CellRangeAddress(endRow - 2, endRow - 2,
                        beginColumn, columnNumber - 1);
                sheet.addMergedRegion(region);
            }
        }
        indexList.add(rowNumber);
        return columnNumber;
    }

    private static void fillHeaderData(Row row, CellStyle style, List headers, Integer columnNumber) {
        for (String header : headers) {
            Cell cell = row.createCell(columnNumber);
            setCell(cell, header, style);
            columnNumber++;
        }
    }

    private static void processExcelNameRow(Sheet sheet, Row excelNameRow, CellStyle style,
                                            Integer columnNumber, String excelName) {
        excelNameRow.setHeightInPoints(EXCEL_NAME_ROW_HEIGHT);
        for (int i = 0; i < columnNumber; i++) {
            Cell cell = excelNameRow.createCell(i);
            cell.setCellStyle(style);
        }

        // excel名称所在行合并
        excelNameRow.getCell(0).setCellValue(excelName);
        CellRangeAddress region = new CellRangeAddress(0, 0, 0, columnNumber - 1);
        sheet.addMergedRegion(region);
    }

    private static void processHeaderRow(Sheet sheet, Integer beginRow, Integer rowNumber, Integer columnNumber) {
        Row headerRow = sheet.getRow(rowNumber - 1);

        // 合并一次表头
        for (int i = 0; i < columnNumber; i++) {
            Cell cell = headerRow.getCell(i);
            if (cell == null) {
                Integer rowIndex = isExistValueBetween(sheet, beginRow, rowNumber, i);
                CellRangeAddress cellAddresses;
                if (rowIndex == null) {
                    cellAddresses = new CellRangeAddress(beginRow, rowNumber - 1, i, i);
                } else {
                    cellAddresses = new CellRangeAddress(rowIndex, rowNumber - 1, i, i);
                }
                sheet.addMergedRegion(cellAddresses);
            }
        }
        // setRowHeight(sheet, beginRow, rowNumber);
    }

    private static Integer isExistValueBetween(Sheet sheet,
                                               int beginRow, int endRow, int columnNumber) {
        if (endRow > beginRow) {
            for (int j = beginRow; j < endRow; j++) {
                Row hssfRow = sheet.getRow(j);
                Cell hssfCell = hssfRow.getCell(columnNumber);
                if (j > beginRow && hssfCell != null) {
                    return j;
                }
            }
        }
        return null;
    }

    /**
     * Process special key.
     *
     * @param sheet         sheet
     * @param specialKeyMap special key
     * @param rowNumber     row number
     * @param columnNumber  column number
     */
    private static void processSpecialKey(Sheet sheet, LinkedHashMap specialKeyMap,
                                          Integer rowNumber, Integer columnNumber) {
        // 处理表头中的特殊键
        if (specialKeyMap != null && specialKeyMap.size() > 0) {
            for (int i = 0; i < rowNumber; i++) {
                Row hssfRow = sheet.getRow(i);
                for (int j = 0; j < columnNumber; j++) {
                    Cell hssfCell = hssfRow.getCell(j);
                    if (hssfCell != null) {
                        String cellValue = hssfCell.getStringCellValue();
                        if (StringUtils.isNotBlank(cellValue)) {
                            specialKeyMap.forEach((key, value) -> {
                                if (hssfCell.getStringCellValue().contains(key)) {
                                    // 只替换key 例如: lastYear XXX 只替换 lastYear
                                    hssfCell.setCellValue(cellValue.replace(key, value));
                                }
                            });
                        }
                    }
                }
            }
        }
    }

    /**
     * Fill excel data and set column style and width.
     *
     * @param sheet        sheet
     * @param style        style
     * @param rowNumber    begin row number
     * @param columnNumber column number
     * @param data         data
     * @param excelFields  fields
     */
    private static void fillExcelData(Sheet sheet, CellStyle style,
                                      Integer rowNumber, Integer columnNumber,
                                      Collection data, List excelFields) {
        int beginRow = rowNumber;
        WIDTH_DATA = calculateHeaderColumnWidth(sheet, beginRow, columnNumber);
        fillExcelData(sheet, style, rowNumber, 0, data, excelFields, 0);
        setColumnWidth(sheet, style);
        WIDTH_DATA.clear();
    }

    /**
     * Fill excel data.
     *
     * @param data         object List
     * @param sheet        sheet
     * @param style        style
     * @param rowNumber    row
     * @param columnNumber column
     * @param level        level
     * @param excelFields  excelFields
     * @return row number
     */
    private static Integer fillExcelData(Sheet sheet, CellStyle style,
                                         Integer rowNumber, Integer columnNumber,
                                         Collection data, List excelFields, Integer level) {

        level++;
        Row row;
        List fields = getFields(level, excelFields);
        Integer beginColumn = getBeginColumn(level, excelFields);
        if (CollectionUtils.isNotEmpty(data)) {
            if (data.toArray()[0] instanceof MultilevelData) {
                List list = convertMultilevelDataList(data);
                for (int i = 0; i < list.size(); i++) {
                    int beginRow = rowNumber;
                    MultilevelData structure = list.get(i);
                    if (sheet.getRow(beginRow) != null) {
                        row = sheet.getRow(beginRow);
                    } else {
                        row = sheet.createRow(beginRow);
                    }
                    if (i == 0) {
                        columnNumber = fillRowData(row, style, structure, fields, beginColumn);
                    } else {
                        fillRowData(row, style, structure, fields, beginColumn);
                    }

                    if (CollectionUtils.isNotEmpty(structure.getChildren())) {
                        rowNumber = fillExcelData(sheet, style,
                                beginRow, columnNumber, structure.getChildren(), excelFields, level);
                    } else {
                        // setRowHeight(sheet, rowNumber, rowNumber + 1);
                        calculateWidth(sheet, rowNumber, rowNumber + 1);
                        rowNumber++;
                    }

                    // 合并列
                    int endRow = rowNumber;
                    int endColumn = columnNumber;
                    if (endRow != 0 && endRow - beginRow > 1) {
                        for (int k = beginColumn; k < endColumn; k++) {
                            CellRangeAddress thirdRegion = new CellRangeAddress(beginRow, endRow - 1, k, k);
                            sheet.addMergedRegion(thirdRegion);
                        }
                    }
                }
            } else {
                List objects = ExcelExportUtil.transformObjectList(data);
                if (CollectionUtils.isNotEmpty(objects) && CollectionUtils.isNotEmpty(fields)) {
                    fillExcel(sheet, style, objects, rowNumber, columnNumber, fields);
                    rowNumber = rowNumber + objects.size();
                } else {
                    // setRowHeight(sheet, rowNumber, rowNumber + 1);
                    calculateWidth(sheet, rowNumber, rowNumber + 1);
                    rowNumber++;
                }
            }
        }
        return rowNumber;
    }

    private static List getFields(Integer level, List excelFields) {
        List fields = new ArrayList<>();
        excelFields.forEach(excelField -> {
            if (level.equals(excelField.getLevel())
                    && !EXCLUDE_KEY.equals(excelField.getFieldName())) {
                fields.add(excelField.getFieldName());
            }
        });
        return fields;
    }

    /**
     * Get begin column from fieldMap.
     *
     * @param level       level
     * @param excelFields excelFields
     * @return fieldMap
     */
    private static Integer getBeginColumn(Integer level, List excelFields) {
        int count = 0;
        for (int i = 1; i < level; i++) {
            List fields = getFields(i, excelFields);
            count = count + fields.size();
        }
        return count;
    }

    /**
     * Fill row data.
     *
     * @param row            row
     * @param style          style
     * @param multilevelData node
     * @param fields         fields
     * @param columnNumber   begin columnNumber
     * @return end columnNumber
     */
    private static Integer fillRowData(Row row, CellStyle style, MultilevelData multilevelData,
                                       List fields, Integer columnNumber) {
        for (String field : fields) {
            if (EXCLUDE_KEY.equals(field)) {
                continue;
            }
            Object value = getValueFromMap(multilevelData.getProperties(), field);
            Cell cell = row.createCell(columnNumber);
            setCell(cell, value, style);
            columnNumber++;

        }
        return columnNumber;
    }

    /**
     * Get value from properties map.
     *
     * @param propertiesMap properties map
     * @param fieldName     fieldName
     * @return object
     */
    private static Object getValueFromMap(HashMap propertiesMap, String fieldName) {
        return propertiesMap.get(fieldName);
    }

    private static void calculateWidth(Sheet sheet, Integer beginRow, Integer endRow) {
        List widthList = calculateColumnWidth(sheet, beginRow, endRow, COLUMN_NUMBER);
        WIDTH_DATA = contrastWidthList(WIDTH_DATA, widthList, COLUMN_NUMBER);
    }

    /**
     * Fill excel. 100 pieces of data to fill excel.
     *
     * @param sheet       sheet
     * @param style       style
     * @param list        data
     * @param beginRow    begin row
     * @param beginColumn begin column
     * @param fields      fields
     * @param          T
     */
    private static  void fillExcel(Sheet sheet, CellStyle style, List list,
                                      Integer beginRow, Integer beginColumn,
                                      List fields) {
        int maxSize = DATA_SIZE;
        int quantity = 0;
        int limit;
        if (beginRow.equals(END_HEADER_ROW)) {
            limit = maxSize - END_HEADER_ROW;
        } else {
            limit = maxSize;
        }

        if (CollectionUtils.isNotEmpty(list)) {
            int size = list.size();
            if (size > limit) {
                size = size - limit;
                quantity++;
            }
            quantity = quantity + size / DATA_SIZE;
            if (size % DATA_SIZE != 0) {
                quantity = quantity + 1;
            }
        }

        List data;
        for (int i = 1; i <= quantity; i++) {
            if (i == 1) {
                data = list.stream().limit(limit).collect(Collectors.toList());
            } else {
                int skipValue = limit;
                limit = limit + maxSize;
                data = list.stream().limit(limit).skip(skipValue).collect(Collectors.toList());
            }

            // 填充内容
            for (int j = 0; j < data.size(); j++) {
                Row row;
                if (sheet.getRow(j + beginRow) != null) {
                    row = sheet.getRow(j + beginRow);
                } else {
                    row = sheet.createRow(j + beginRow);
                }

                // 获取单个对象
                T item = data.get(j);

                Cell cell;
                for (int k = 0; k < fields.size(); k++) {
                    if (EXCLUDE_KEY.equals(fields.get(k))) {
                        continue;
                    }
                    cell = row.createCell(k + beginColumn);
                    Object objValue = getValueByFieldName(fields.get(k), item);
                    setCell(cell, objValue, style);
                }
            }
            int endRow = beginRow + data.size();
            // setRowHeight(sheet, beginRow, endRow);
            calculateWidth(sheet, beginRow, endRow);
            beginRow = endRow;
        }
    }

    /**
     * set excel cell value and style.
     *
     * @param cell     excel cell
     * @param objValue value
     * @param style    cell style
     */
    private static void setCell(Cell cell, Object objValue, CellStyle style) {
        if (objValue instanceof Date) {
            String value = new SimpleDateFormat(DEFAULT_DATE_PATTEN).format(objValue);
            cell.setCellValue(value);
        } else if (objValue == null) {
            cell.setBlank();
        } else if (objValue instanceof Number) {
            double value = Double.parseDouble(String.valueOf(objValue));
            cell.setCellValue(value);
        } else if (objValue instanceof Boolean) {
            Boolean value = Boolean.valueOf(String.valueOf(objValue));
            cell.setCellValue(convertBooleanValue(value));
        } else {
            String value = String.valueOf(objValue);
            cell.setCellValue(getTrueAndFalseValue(value));
        }
        cell.setCellStyle(style);
    }

    /**
     * Set row height.
     *
     * @param sheet    sheet
     * @param beginRow begin row
     * @param endRow   end row
     */
    private static void setRowHeight(Sheet sheet, Integer beginRow, Integer endRow) {
        for (int i = beginRow; i < endRow; i++) {
            Row row = sheet.getRow(i);
            row.setHeightInPoints(DEFAULT_ROW_HEIGHT);
        }
    }

    /**
     * Set column width and style.
     *
     * @param sheet sheet
     * @param style style
     */
    private static void setColumnWidth(Sheet sheet, CellStyle style) {
        Integer width;
        for (int i = 0; i < WIDTH_DATA.size(); i++) {
            width = WIDTH_DATA.get(i);
            if (width < 8) {
                width = width * 2;
            }
            width = width * 256 + 184;
            sheet.setColumnWidth(i, width);
            sheet.setDefaultColumnStyle(i, style);
        }
    }

    /**
     * Set auto column width.
     *
     * @param sheet sheet
     * @param style style
     */
    private static void setAutoColumnWidth(Sheet sheet, CellStyle style) {
        // 设置自动列宽
        // 自动列宽 - 填充完成设置
        SXSSFSheet sxssfSheet = (SXSSFSheet) sheet;
        for (int i = 0; i < COLUMN_NUMBER; i++) {
            sxssfSheet.trackColumnForAutoSizing(i);
            sxssfSheet.autoSizeColumn(i, Boolean.TRUE);
            sxssfSheet.setDefaultColumnStyle(i, style);
        }
    }

    /**
     * Calculate column width.
     *
     * @param sheet        sheet
     * @param beginRow     begin row
     * @param endRow       end row
     * @param columnNumber column number
     * @return width list
     */
    private static List calculateColumnWidth(Sheet sheet, Integer beginRow, Integer endRow, Integer columnNumber) {
        List list = new ArrayList<>();
        for (int i = 0; i < columnNumber; i++) {
            int maxWidth = 0;
            int width;
            for (int j = beginRow; j < endRow; j++) {
                Row row = sheet.getRow(j);
                width = calculateColumnWidth(row, i);
                if (width > maxWidth) {
                    maxWidth = width;
                }
            }
            list.add(maxWidth);
        }
        return list;
    }

    /**
     * Calculate excel header column width.
     *
     * @param sheet        sheet
     * @param rowNumber    end row
     * @param columnNumber column number
     * @return excel header width list
     */
    private static List calculateHeaderColumnWidth(Sheet sheet, Integer rowNumber, Integer columnNumber) {
        List list = new ArrayList<>();
        for (int i = 0; i < columnNumber; i++) {
            int width;
            Integer rowIndex = isExistValueBetween(sheet, 0, rowNumber, i);
            Row hssfRow;

            if (rowIndex != null && rowIndex != rowNumber - 1) {
                if (sheet.getRow(rowIndex + 1) != null &&
                        sheet.getRow(rowIndex + 1).getCell(i) == null) {
                    hssfRow = sheet.getRow(rowIndex);
                } else {
                    hssfRow = sheet.getRow(rowNumber - 1);
                }
            } else {
                hssfRow = sheet.getRow(rowNumber - 1);
            }
            width = calculateColumnWidth(hssfRow, i);
            list.add(width);
        }
        return list;
    }

    private static Integer calculateColumnWidth(Row row, Integer columnNumber) {
        int maxColumn = 0;
        Cell cell = row.getCell(columnNumber);
        if (cell != null) {
            CellType type = cell.getCellType();
            int length;
            if (type.equals(CellType.NUMERIC)) {
                length = String.valueOf(cell.getNumericCellValue()).length();
                length = length / 2;
            } else {
                // 如果是String 类型还有处理"\n"
                String value = cell.getStringCellValue();
                if (StringUtils.isNotBlank(value)) {
                    int count = getCountStr(value, String.valueOf((char) 10));
                    // 5 / 1 = 5
                    count = count + 1;
                    length = value.length() / count;
                } else {
                    length = 0;
                }
            }

            if (length > maxColumn) {
                maxColumn = length;
            }
        }

        if (maxColumn > 0) {
            /*if (!Boolean.FALSE) {
                if (maxColumn > MAX_COLUMN_WIDTH) {
                    maxColumn = MAX_COLUMN_WIDTH;
                }
            }*/
            if (maxColumn > MAX_COLUMN_WIDTH) {
                maxColumn = MAX_COLUMN_WIDTH;
            }
        }
        return maxColumn;
    }

    /**
     * Contrast A width list and B width list
     *
     * @param firstList  a width list
     * @param secondList b width list
     * @param size       size
     * @return max width list
     */
    private static List contrastWidthList(List firstList, List secondList, Integer size) {
        List maxList = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            Integer max;
            if (firstList.get(i) > secondList.get(i)) {
                max = firstList.get(i);
            } else {
                max = secondList.get(i);
            }
            maxList.add(max);
        }
        return maxList;
    }

    /*private static List calculateDataRowHeight(Sheet sheet, Integer rowNumber, Integer columnNumber,
                                                        Integer beginRow, List headerColumnWidths) {
        List data = new ArrayList<>();
        for (int i = beginRow; i < rowNumber; i++) {
            List max = new ArrayList<>();
            Row hssfRow = sheet.getRow(i);
            int rowHeight;
            for (int j = 0; j < columnNumber; j++) {
                Integer width = calculateColumnWidth(hssfRow, j, Boolean.TRUE);
                if (width == 0) {
                    rowHeight = 1;
                } else {
                    Integer columnWidth = headerColumnWidths.get(j);
                    if (columnWidth == 0) {
                        rowHeight = 1;
                    } else {
                        if (width % columnWidth != 0) {
                            rowHeight = width / columnWidth + 1;
                        } else {
                            rowHeight = width / columnWidth;
                        }
                    }
                }
                max.add(rowHeight);
            }
            data.add(Collections.max(max));
        }
        return data;
    }*/

    /**
     * Set default cell style and set font.
     *
     * @param wb wb
     * @return cell style
     */
    private static CellStyle setDefaultStyle(Workbook wb) {
        CellStyle style = wb.createCellStyle();
        Font font = wb.createFont();

        // 设置字体 Arial
        font.setFontName(DEFAULT_FONT_NAME);
        // 设置字体大小
        font.setFontHeightInPoints((short) 10);
        style.setFont(font);

        // 设置样式
        setDefaultCellStyle(style);
        return style;
    }

    /**
     * Set default excel cell style.
     *
     * @param style excel cell style
     */
    private static void setDefaultCellStyle(CellStyle style) {
        // 水平居中
        style.setAlignment(HorizontalAlignment.CENTER);
        // 垂直居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 底边框
        style.setBorderBottom(BorderStyle.THIN);
        // 左边框
        style.setBorderLeft(BorderStyle.THIN);
        // 上边框
        style.setBorderTop(BorderStyle.THIN);
        // 右边框
        style.setBorderRight(BorderStyle.THIN);
        //缩进
        //style.setIndention((short)5);
    }

    /**
     * process excel name.
     *
     * @param excelName excel name
     * @return process result
     */
    private static String processExcelName(String excelName) {
        if (StringUtils.isBlank(excelName)) {
            excelName = String.valueOf(System.currentTimeMillis());
        }
        return excelName;
    }

    /**
     * set response header and content type for write file io.
     *
     * @param response HttpServletResponse
     */
    private static void processExcelResponse(HttpServletResponse response) {
        try {
            // 清除buffer缓存
            response.reset();
            // 设置编码格式
            response.setCharacterEncoding(DEFAULT_ENCODING);
            // 输出excel文件
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
            /*response.setHeader("Content-Transfer-Encoding", "binary");*/
            String fileName = URLEncoder.encode(String.valueOf(System.currentTimeMillis()), DEFAULT_ENCODING);
            response.setHeader("Content-Disposition", "attachment; filename=\"" +
                    fileName + DEFAULT_SUFFIX + "\"");
            /*response.setContentType("APPLICATION/OCTET-STREAM");*/
            response.setHeader("Content-type", "text/html");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    private static String getTrueAndFalseValue(String value) {
        if (StringUtils.isBlank(value)) {
            return value;
        } else {
            if ("true".equalsIgnoreCase(value)) {
                return convertBooleanValue(Boolean.valueOf(value));
            } else if ("false".equalsIgnoreCase(value)) {
                return convertBooleanValue(Boolean.valueOf(value));
            }
        }
        return value;
    }

    private static String convertBooleanValue(Boolean value) {
        if (value) {
            return "是";
        } else {
            return "否";
        }
    }

    /**
     * Output response stream.
     *
     * @param response response
     * @param wb       workBook
     */
    private static void outputResponseStream(HttpServletResponse response, Workbook wb) {
        try {
            // 将文件输出
            OutputStream outputStream = response.getOutputStream();
            wb.write(outputStream);

            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * Output file.
     *
     * @param wb workBook
     */
    private static void outputFileStream(Workbook wb) {
        try {
            //Test
            // 创建文件输出流对象
            FileOutputStream fos = new FileOutputStream("D:" + File.separator +
                    System.currentTimeMillis() + DEFAULT_SUFFIX);
            wb.write(fos);

            // 关闭此文件输出流并释放资源
            fos.flush();
            fos.close();
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * Close work book.
     *
     * @param wb wb
     */
    private static void closeWorkBook(Workbook wb) {
        try {
            wb.close();
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * convert to List.
     *
     * @param children collection
     * @return List
     */
    private static List convertMultilevelDataList(Collection children) {
        List multilevelData = new ArrayList<>();
        if (children.toArray()[0] instanceof MultilevelData) {
            children.forEach(child -> multilevelData.add((MultilevelData) child));
        }
        return multilevelData;
    }

    /**
     * Convert to object list.
     *
     * @param objects objects
     * @param      Model
     * @return List
     */
    public static  List transformObjectList(Collection objects) {
        return new ArrayList<>(Arrays.asList(objects.toArray()));
    }

    /**
     * Get field by field name.
     *
     * @param fieldName field name
     * @param clazz     class
     * @return field
     */
    public static Field getFieldByName(String fieldName, Class clazz) {
        if (clazz != null && clazz != Object.class) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getName().equals(fieldName)) {
                    return field;
                }
            }
            Class superClazz = clazz.getSuperclass();
            if (superClazz != null && superClazz != Object.class) {
                return getFieldByName(fieldName, superClazz);
            }
        }
        return null;
    }

    /**
     * Get value by field.
     *
     * @param fieldName Field
     * @param object    object
     * @return object
     */
    public static Object getValueByFieldName(String fieldName, Object object) {
        if (object != null) {
            Field field = getFieldByName(fieldName, object.getClass());
            if (field != null) {
                try {
                    field.setAccessible(Boolean.TRUE);
                    return field.get(object);
                } catch (IllegalAccessException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
        return null;
    }

    /**
     * Convert to MultilevelData List.
     *
     * @param collection collection
     * @param nestedName nested property name
     * @param outerClass className
     * @return collection
     */
    public static Collection convertMultilevelData(Collection collection, String nestedName,
                                                      Class outerClass) {
        List fields = getAllFields(outerClass);
        if (CollectionUtils.isNotEmpty(fields)) {
            return convertMultilevelData(collection, nestedName, outerClass, fields);
        } else {
            return collection;
        }
    }

    /**
     * Get all fields include SuperClass.
     *
     * @param clazz class
     * @return All fields
     */
    public static List getAllFields(Class clazz) {
        List fieldList = new ArrayList<>();
        if (clazz != null && clazz != Object.class) {
            Field[] fields = clazz.getDeclaredFields();
            if (fields.length > 0) {
                fieldList.addAll(Arrays.asList(fields));
            }

            if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
                fieldList.addAll(getAllFields(clazz.getSuperclass()));
            }
        }
        return fieldList;
    }

    /**
     * Convert to MultilevelData List.
     *
     * @param collection collection
     * @param nestedName nested property name
     * @param outerClass className
     * @param fields     fields
     * @return collection
     */
    private static Collection convertMultilevelData(Collection collection, String nestedName,
                                                       Class outerClass, List fields) {
        List dataStructures = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(collection)) {
            Object object = collection.toArray()[0];
            if (object.getClass().equals(outerClass)) {
                for (Object collect : collection) {
                    MultilevelData multilevelData = new MultilevelData();
                    HashMap hashMap = new HashMap<>();
                    for (Field field : fields) {
                        field.setAccessible(Boolean.TRUE);
                        try {
                            Object value = field.get(collect);
                            if (field.getName().equals(nestedName)) {
                                if (value != null) {
                                    multilevelData.setChildren(
                                            convertMultilevelData((Collection) value, nestedName, outerClass, fields));
                                }
                            } else {
                                hashMap.put(field.getName(), value);
                            }
                        } catch (IllegalAccessException e) {
                            LOGGER.error(e.getMessage(), e);
                        }
                    }
                    multilevelData.setProperties(hashMap);
                    dataStructures.add(multilevelData);
                }
            } else {
                return collection;
            }
        }
        return dataStructures;
    }

    private static List convertExcelHeaderList(Collection excelHeaders) {
        List headers = new ArrayList<>();
        if (excelHeaders.toArray()[0] instanceof ExcelHeader) {
            excelHeaders.forEach(
                    header -> headers.add((ExcelHeader) header)
            );
        }
        return headers;
    }

    private static Integer getCountStr(String str, String connector) {
        return getCountStr(str, connector, 0);
    }

    private static Integer getCountStr(String str, String connector, Integer count) {
        if (str.length() > 0) {
            int index = str.indexOf(connector);
            if (index > -1) {
                count++;
            }
            if (index > -1) {
                str = str.substring(index + 1);
                count = getCountStr(str, connector, count);
            }
        }
        return count;
    }
}

    private static List buildExcelHeaderList(LinkedHashMap> headerMap) {
        List headerList = new ArrayList<>();
        Integer level = 1;
        LinkedHashMap map = getMap(level, headerMap);
        if (map != null && map.size() > 0) {
            for (Map.Entry entry : map.entrySet()) {
                com.stream.am.entity.excel.ExcelHeader header = new com.stream.am.entity.excel.ExcelHeader();
                header.setName(entry.getValue());
                header.setChildren(generateChildren(level, String.valueOf(entry.getKey()), headerMap));
                headerList.add(header);
            }
        }
        return headerList;
    }

    private static List generateChildren(Integer level, String key,
                                                                                 LinkedHashMap> headerMap) {
        level++;
        LinkedHashMap linkedHashMap = getMap(level, headerMap);
        List headerList = new ArrayList<>();
        if (linkedHashMap != null && linkedHashMap.size() > 0) {
            for (Map.Entry entry : linkedHashMap.entrySet()) {
                if (entry.getKey().startsWith(key)) {
                    int index = entry.getKey().indexOf(key);
                    String s = entry.getKey().substring(index + key.length(), index + key.length() + 1);
                    if (s.equals(CONNECTOR)) {
                        com.stream.am.entity.excel.ExcelHeader header = new com.stream.am.entity.excel.ExcelHeader();
                        header.setName(entry.getValue());
                        header.setChildren(generateChildren(level, entry.getKey(), headerMap));
                        headerList.add(header);
                    }
                }
            }
        }
        return headerList;
    }

    private static List buildExcelFieldList(LinkedHashMap> fieldMap) {
        List excelFields = new ArrayList<>();
        if (fieldMap != null) {
            int level = 1;
            for (int i = level; i < level + fieldMap.size(); i++) {
                LinkedHashMap map = getMap(i, fieldMap);
                if (map != null) {
                    for (Map.Entry entry : map.entrySet()) {
                        com.stream.am.entity.excel.ExcelField excelField = new com.stream.am.entity.excel.ExcelField();
                        excelField.setLevel(i);
                        excelField.setFieldName(entry.getKey());
                        excelFields.add(excelField);
                    }
                }
            }
        }
        return excelFields;
    }

    /**
     * Get map.
     *
     * @param level level
     * @param map   map
     * @return map
     */
    private static LinkedHashMap getMap(Integer level,
                                                        LinkedHashMap> map) {
        return map.get(level);
    }
 
  

4.使用示例

    public static void main(String[] args) {
        List list = new ArrayList<>();
        
        Collection data = convertMultilevelDataStructure(list);

        LinkedHashMap specialKeyMap = new LinkedHashMap<>();
        String excelName = "角色信息表";
        String suffix = ".xls";

        LinkedHashMap firstHeader = new LinkedHashMap<>();
        LinkedHashMap secondHeader = new LinkedHashMap<>();
        LinkedHashMap thirdHeader = new LinkedHashMap<>();
        LinkedHashMap> headerMap = new LinkedHashMap<>();

        // 代码会将此处的value值填充到表头
        firstHeader.put("1", "名称");
        firstHeader.put("2", "描述");
        firstHeader.put("3", "时间");

        // 假设表头时间下存在创建时间和更新时间
        secondHeader.put("3_1", "创建时间");
        secondHeader.put("3_2", "更新时间");

        // 假设创建时间下还有备注
        thirdHeader.put("3_1_1", "备注");

        // 然后统一放入headerMap 从1开始代表层数从1开始
        headerMap.put(1, firstHeader);
        headerMap.put(2, secondHeader);
        headerMap.put(3, thirdHeader);

        LinkedHashMap firstField = new LinkedHashMap<>();

        LinkedHashMap> fieldMap = new LinkedHashMap<>();

        firstField.put("name", "此处的value值不重要,key重要");
        firstField.put("description", "此处的value值不重要,key重要");
        firstField.put("createdAt", "此处的value值不重要,key重要");
        firstField.put("updatedAt", "此处的value值不重要,key重要");
        firstField.put("remarks", "此处的value值不重要,key重要");

        fieldMap.put(1, firstField);

        // response 在控制层放入方法参数中
        // @RequestMapping(value = "/role/excel/export", method = RequestMethod.GET)
        // public void export(HttpServletResponse response){}
        HttpServletResponse response = null;

        exportExcel(data, buildExcelHeaderList(headerMap),
                buildExcelFieldList(fieldMap), specialKeyMap, excelName, suffix, response);
    }

在这里插入图片描述
Java Poi 导出功能优化_第4张图片
Java Poi 导出功能优化_第5张图片
Java Poi 导出功能优化_第6张图片
Java Poi 导出功能优化_第7张图片

5.注意:

  • 可以使用静态的Header类来加载上面的东西。
static {
   
       String excelName = "角色信息表";
       String suffix = ".xlsx";
   
       LinkedHashMap firstHeader = new LinkedHashMap<>();
       LinkedHashMap secondHeader = new LinkedHashMap<>();
       LinkedHashMap thirdHeader = new LinkedHashMap<>();
       LinkedHashMap> headerMap = new LinkedHashMap<>();
   
       // 代码会将此处的value值填充到表头
       firstHeader.put("1", "名称");
       firstHeader.put("2", "描述");
       firstHeader.put("3", "时间");
   
       // 加入表头时间下存在创建时间和更新时间
       secondHeader.put("3_1", "创建时间");
       secondHeader.put("3_2", "更新时间");
   
       // 加入创建时间下还有备注
       thirdHeader.put("3_1_1", "备注");
   
       // 然后统一放入headerMap 从1开始代表层数从1开始
       headerMap.put(1, firstHeader);
       headerMap.put(2, secondHeader);
       headerMap.put(3, thirdHeader);
   
       LinkedHashMap firstField = new LinkedHashMap<>();
   
       LinkedHashMap> fieldMap = new LinkedHashMap<>();
   
       firstField.put("name", "此处的value值不重要,key重要");
       firstField.put("description", "此处的value值不重要,key重要");
       firstField.put("createdAt", "此处的value值不重要,key重要");
       firstField.put("updatedAt", "此处的value值不重要,key重要");
       firstField.put("remarks", "此处的value值不重要,key重要");
   
       fieldMap.put(1, firstField); 
  } 
  • 表头可以是聚合表头,也可以是一个String集合,或者ExcelHeader集合。表头是有层数,层数从1开始。
  • ExcelHeader中Index属性示例1,1_1,1_1_1,代表第一层,第二层,第三层,index属性放的是上面headerMap的key值,name属性是headerMap的value值。name属性是对应中文汉字。表头可以用ExcelHeader类继续嵌套。表头可以是N层。是一个树形结构。
  • ExcelField也需要有层数,与数据对应,取不到值就是空白,层数同样从1开始。ExcelField中level属性代表层数,从1开始,fieldName代表对应字段,数据要么是从键值对中获取,要么是使用反射获取。
  • ExcelHeader 和ExcelField是强对应,层数都是从1开始。
  • 下面的数据使用MultilevelData存放,只有两个属性,一个是键值对,另一个是泛型的集合,也可以是多层,可以为嵌套,属性不定。也可以是List<实体类>,比如简单的Student类,没有出现任何嵌套的类。
  • 表头可能出现特殊key,可以放一个键值对到specialKeyMap使用代码解析。
  • 前端可以将表头和列传到后端,但如果太长就要使用post方式传递。get方式,url有长度限制。get方式传的是字符串,要考虑如何将字符串转成对应的类。
  • 因为写的是通用的excel导出方法,所以没有页眉页脚,以及计算公式,单元格锁定 ,等等。如果有需要可以往上面继续添加功能。
  • 每个嵌套的属性类属性不一致,不能使用同一个类,所以使用MultilevelData类。键值对中可以放入任意属性,children属性也是泛型,可以放任意集合。此处的任意集合是那种简单的类集合。如果还是嵌套的类,可以转换成MultilevelDataStructure。
    6.结语,创作不易,随手点赞,手有余香。

你可能感兴趣的:(Java,POI)