【开发心得】EasyExcel导入导出

概述:
使用 easyExcel 3.x版本导出参考

环境:

jdk1.8

Springboot 2.5.7 

easyExcel 3.0.5

easyPoi 5.0.0

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.0.5</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml-schemas</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.xmlgraphics</groupId>
                    <artifactId>batik-all</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.santuario</groupId>
                    <artifactId>xmlsec</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>de.rototor.pdfbox</groupId>
                    <artifactId>graphics2d</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

【模板类参考.脱敏】

@Data
@NoArgsConstructor
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER) // 表头样式
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER) // 内容样式
@HeadFontStyle(bold = BooleanEnum.TRUE,color = -1)
public class DataOutput {
    @ColumnWidth(value = 20)
    @ExcelProperty(value = {"id"} ,index = 0)
    private String uuid;
    @ColumnWidth(value = 20)
    @ExcelProperty(value = {"名称"} ,index = 1)
    private String name;
    @ContentStyle(dataFormat = 49)
    @ColumnWidth(value = 20)
    @ExcelProperty(value = {"时间点"} ,index = 2)
    private Long materialId;
    @ExplicitConstraint(source = {"类型1", "类型2", "类型3"}) // 固定下拉内容
    @ColumnWidth(value = 20)
    @ExcelProperty(value = {"类型"} ,index = 4)
    private String type;
    @ExplicitConstraint(sourceClass = ScopeExplicitConstraint.class) // 动态下拉内容
    @ColumnWidth(value = 20)
    @ExcelProperty(value = {"使用范围"} ,index = 8)
    private String scope;
}

【合并列】

package com.xxx.output;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.metadata.CellExtra;
import com.xxx.UploadDataListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.util.List;

/**
 * @author
 * @date 2020-12-01 13:34
 **/
@Slf4j
public class ExcelAnalysisHelper<T> {
    public List<T> getList(File file, Class<T> clazz) {
        try (InputStream inputStream = new FileInputStream(file)) {
            return getList(inputStream, clazz, 0, 1);
        } catch (FileNotFoundException e) {
            log.error("file not found:{}", e.getMessage());
        } catch (IOException e) {
            log.error("file io exception:{}", e.getMessage());
        }
        return null;
    }

    public List<T> getList(InputStream inputStream, Class<T> clazz, Integer sheetNo, Integer headRowNumber) {
        UploadDataListener<T> listener = new UploadDataListener<>(headRowNumber);
        EasyExcel.read(inputStream, clazz, listener).extraRead(CellExtraTypeEnum.MERGE).sheet(sheetNo).headRowNumber(headRowNumber).doRead();
        List<CellExtra> extraMergeInfoList = listener.getExtraMergeInfoList();
        if (CollectionUtils.isEmpty(extraMergeInfoList)) {
            return listener.getData();
        }
        List<T> data = explainMergeData(listener.getData(), extraMergeInfoList, headRowNumber);
        return data;
    }

    /**
     * 处理合并单元格
     *
     * @param data               解析数据
     * @param extraMergeInfoList 合并单元格信息
     * @param headRowNumber      起始行
     * @return 填充好的解析数据
     */
    private List<T> explainMergeData(List<T> data, List<CellExtra> extraMergeInfoList, Integer headRowNumber) {
//        循环所有合并单元格信息
        extraMergeInfoList.forEach(cellExtra -> {
            int firstRowIndex = cellExtra.getFirstRowIndex() - headRowNumber;
            int lastRowIndex = cellExtra.getLastRowIndex() - headRowNumber;
            int firstColumnIndex = cellExtra.getFirstColumnIndex();
            int lastColumnIndex = cellExtra.getLastColumnIndex();
//            获取初始值
            Object initValue = getInitValueFromList(firstRowIndex, firstColumnIndex, data);
//            设置值
            for (int i = firstRowIndex; i <= lastRowIndex; i++) {
                for (int j = firstColumnIndex; j <= lastColumnIndex; j++) {
                    setInitValueToList(initValue, i, j, data);
                }
            }
        });
        return data;
    }

    /**
     * 设置合并单元格的值
     *
     * @param filedValue  值
     * @param rowIndex    行
     * @param columnIndex 列
     * @param data        解析数据
     */
    public void setInitValueToList(Object filedValue, Integer rowIndex, Integer columnIndex, List<T> data) {
        T object = data.get(rowIndex);

        for (Field field : object.getClass().getDeclaredFields()) {
            // 提升反射性能,关闭安全检查
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == columnIndex) {
                    try {
                        field.set(object, filedValue);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("analysis data error: {}", e.getMessage());
                    }
                }
            }
        }
    }


    /**
     * 获取合并单元格的初始值
     * rowIndex对应list的索引
     * columnIndex对应实体内的字段
     *
     * @param firstRowIndex    起始行
     * @param firstColumnIndex 起始列
     * @param data             列数据
     * @return 初始值
     */
    private Object getInitValueFromList(Integer firstRowIndex, Integer firstColumnIndex, List<T> data) {
        Object filedValue = null;
        T object = data.get(firstRowIndex);
        for (Field field : object.getClass().getDeclaredFields()) {
            // 提升反射性能,关闭安全检查
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                if (annotation.index() == firstColumnIndex) {
                    try {
                        filedValue = field.get(object);
                        break;
                    } catch (IllegalAccessException e) {
                        log.error("analysis error:{}", e.getMessage());
                    }
                }
            }
        }
        return filedValue;
    }
}
package com.xxx.output;

/**
 * @Author: martin
 * @Date: 2021/12/15 6:36 下午
 * @Description:
 */

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @author
 * @date
 */
public class ExcelFillCellMergeStrategy implements CellWriteHandler {

    private int[] mergeColumnIndex;
    private int mergeRowIndex;

    public ExcelFillCellMergeStrategy() {
    }

    public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
        //当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();
        if (curRowIndex > mergeRowIndex) {
            Cell firstCell = cell.getRow().getCell(0); // 当前行第一个cell
            Cell preFirstCell = cell.getSheet().getRow(curRowIndex - 1).getCell(0); // 上一行第一个cell
            if (firstCell != null && preFirstCell != null) {
                if (!firstCell.getStringCellValue().equals(preFirstCell.getStringCellValue())) {
                    return; // 首行不一致,跳过合并
                }
            }
            for (int i = 0; i < mergeColumnIndex.length; i++) {
                if (curColIndex == mergeColumnIndex[i]) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
        if (cell.getCellType() == CellType.BLANK) {
            return;
        }
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
        // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
        if (curData.equals(preData)) {
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

【下拉框-动态】

package com.xxx.output;

import java.lang.annotation.*;

/**
 * @Author: martin
 * @Date: 2021-12-22 10:46
 * @Description:
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ExplicitConstraint {
    //定义固定下拉内容
    String[]source()default {};
    //定义动态下拉内容,
    Class[]sourceClass()default {};
}
package com.xxx.output;

/**
 * @Author: martin
 * @Date: 2021-12-22 10:47
 * @Description:
 */
public interface ExplicitInterface {
    /**
     * 下拉列表的内容数组
     * @return
     */
    String[] source();
}

package com.xxx.output;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: martin
 * @Date: 2021-12-22 11:08
 * @Description:
 */
public class ExcelExplicitStrategy implements SheetWriteHandler {
    // 处理下拉列表
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 下拉列表集合
        Map<Integer, String[]> explicitListConstraintMap = new HashMap<>();
        // 循环获取对应列得下拉列表信息
        Field[] declaredFields = XXX.class.getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            Field field = declaredFields[i];
            // 解析注解信息
            ExplicitConstraint explicitConstraint = field.getAnnotation(ExplicitConstraint.class);
            String[] explicitArray = resolveExplicitConstraint(explicitConstraint);
            if (explicitArray != null && explicitArray.length > 0) {
                explicitListConstraintMap.put(i, explicitArray);
            }
            // 通过sheet处理下拉信息
            Sheet sheet = writeSheetHolder.getSheet();
            DataValidationHelper helper = sheet.getDataValidationHelper();
            explicitListConstraintMap.forEach((k, v) -> {
                CellRangeAddressList rangeList = new CellRangeAddressList();
                CellRangeAddress addr = new CellRangeAddress(1, 1000, k, k);
                rangeList.addCellRangeAddress(addr);
                DataValidationConstraint constraint = helper.createExplicitListConstraint(v);
                DataValidation validation = helper.createValidation(constraint, rangeList);
                sheet.addValidationData(validation);
            });
        }
    }

    /**
     * 解析注解内容 获取下列表信息
     *
     * @param explicitConstraint
     * @return
     */
    public String[] resolveExplicitConstraint(ExplicitConstraint explicitConstraint) {
        if (explicitConstraint == null) {
            return null;
        }
        //固定下拉信息
        String[] source = explicitConstraint.source();
        if (source.length > 0) {
            return source;
        }
        //动态下拉信息
        Class<? extends ExplicitInterface>[] classes = explicitConstraint.sourceClass();
        if (classes.length > 0) {
            ExplicitInterface explicitInterface = null;
            try {
                explicitInterface = classes[0].newInstance();
                String[] source1 = explicitInterface.source();
                if (source1.length > 0) {
                    return source1;
                }
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
public class ScopeExplicitConstraint implements ExplicitInterface {
    @Override
    public String[] source() {
        List<String> list = new ArrayList<>();
        for (ScopeEnum item : ScopeEnum.values()) {
            list.add(item.name);
        }
        return list.toArray(new String[list.size()]);
    }
}

 

【导出参考】

 public void exportData(HttpServletResponse response, List<Long> dataIds) {
        PageRequest pageRequest = PageRequest.of(0, dataIds.size());
        List<xxxOutput> resultList = xxxService.getEditDocument(dataIds, pageRequest);
        try {
//            设置第几列合并
            int[] mergeColumnIndex = {0, 1, 2, 3}; // 合并第一列,所以赋值0,如果需要合并多列,直接逗号分隔:int[] mergeColumnIndex = {0,1,2}
//            需要从第几行开始合并
            int mergeRowIndex = 1;
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            //  设置文件名称
//            String fileName = URLEncoder.encode("xxx", "UTF-8");
            String fileName = this.getXlsFileName();
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            //  sheet名称
            EasyExcel.write(response.getOutputStream(), xxx.class)
//                    excel版本
                    .excelType(ExcelTypeEnum.XLSX)
//                    是否自动关流
                    .autoCloseStream(Boolean.TRUE)
                    .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumnIndex))
                    .registerWriteHandler(new ExcelExplicitStrategy())
                    .sheet("xxx导出").doWrite(resultList);
        } catch (Exception e) {
            log.error("export xxx error: {}", e.getMessage());
        }

    }

posted @ 2021-12-27 19:19  虹梦未来  阅读(21)  评论(0编辑  收藏  举报  来源