需求描述:java后端开发过程中,为了满足动态生成excel模板并设置指定单元格为下拉框,且下拉框的数据项来源为动态查询的需求,在基于easyExcel的情况下,使用模板填充的方式,完成该需求。

1. control层
 
public void getTemp(HttpServletResponse response) throws IOException{
    tempService.getTemp(response);
}
 
2. service层
 
void getTemp(HttpServletResponse response);
 
3. service实现类(模板获取及导出相关配置设定,业务逻辑处理等):
 
@Override
public void getTemp(HttpServletResponse response){
    
    //动态模板需填充的数据
    List<Object> data = new ArrayList<>();

    //下拉列表1数据项
    List<String> selectList1 = new ArrayList<>();
    //下拉列表2数据项
    List<String> selectList2 = new ArrayList<>();

    //将下拉列表数据放置在一个map中
    Map<Integer, List<String>> selectMap = new HashMap<>();
    selectMap.put(1,selectList1);
    selectMap.put(2,selectList2);

    //此处firstRow为需要设置下拉框的起始行位置,可根据自身需求需要调整设置
    //此处lastRow为需要设置下拉框的截止行位置,可根据自身需求需要调整设置
    Integer firstRow = 0;
    Integet lastRow = 100;

    //使用模板填充导出的相关配置
    OutputStream out = null;
    BufferedOutputStream bos = null;
    try {
       //获取到已提前制作好的填充模板
        String templateFileName = FileUtil.getPath() + "template" + File.separator + "Temp.xlsx";

        //为动态生成的模板命名(带时间区分)
        String fileNameWithTime = "导入模板" + DateTimeUtil.formatDate(new Date(), DateTimeUtil.SHORT_TIME_FORMAT) + ".xlsx";

        //设置字符编码标准等
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(fileNameWithTime,"utf-8");
        response.setHeader("Content-disposition", "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));

        out = response.getOutputStream();
        bos = new BufferedOutputStream(out);
        ExcelWriter excelWriter = EasyExcel.write(bos).withTemplate(templateFileName).build();

        //此处用到的"registerWriteHandler()"用于设置相应格式,“SelectSheetWriteHandler(selectMap,firstRow,lastRow)”为封装的设置单元格下拉框的工具类  
        WriteSheet writeSheet = EasyExcel.writerSheet().registerWriteHandler(new SelectSheetWriteHandler(selectMap,firstRow,100)).build();
        
        //向下新增行填充Config
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        excelWriter.fill(data,fillConfig,writeSheet);
        excelWriter.finish();
        bos.flush();

    }catch (IOException e){
        e.printStackTrace();
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");
        try {
            response.getWriter().println("打印失败");
        }catch (IOException ex){
            ex.printStackTrace();
        }
    }
}
 
4. SelectSheetWriteHandler工具类封装
 
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.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.util.List;
import java.util.Map;

/**
 * @author XXX
 * @Description:  处理下拉
 * @date 2022/8/10 17:39
 */
public class SelectSheetWriteHandler implements SheetWriteHandler {
 
    /**
     * 下拉框数据项Map, key为第几列,List<String>为下拉框数据项
     */
    private Map<Integer, List<String>> selectMap;

    /**
     * 设置下拉框位置首行
     */
    private Integer firstRow;

    /**
     * 设置下拉框位置末行
     */
    private Integer lastRow;

    /**
     * 数据字典集
     */
    private char[] alphabet = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
            'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};

    /**
     * @param selectMap 设置下拉框的数据项
     * @param firstRow 设置下拉框的开始行(实质从firstRow的下一行开始生效)
     * @param lastRow 设置下拉框的最后一行
     */
    public SelectSheetWriteHandler(Map<Integer, List<String>> selectMap,Integer firstRow, Integer lastRow){
        this.selectMap = selectMap;
        this.firstRow = firstRow;
        this.lastRow = lastRow;
    }

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder)
    {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        if(selectMap == null || selectMap.size() ==0){
            return;
        }
        Sheet sheet = writeSheetHolder.getSheet();
        /// 开始设置下拉框
        DataValidationHelper helper = sheet.getDataValidationHelper();// 设置下拉框
        String dictSheetName = "字典sheet";
        Workbook workbook = writeWorkbookHolder.getWorkbook();
        //数据字典的sheet页
        Sheet dictSheet = workbook.createSheet(dictSheetName);
        //将数据字典的sheet隐藏(对用户不可见)
        workbook.setSheetHidden(workbook.getSheetIndex(dictSheet),true);
        for (Map.Entry<Integer, List<String>> entry : selectMap.entrySet()) {
            /*** 起始行、终止行、起始列、终止列 **/
            CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, entry.getKey(), entry.getKey());
            int rowLen = entry.getValue().size();
            // 设置字典sheet页的值 每一列一个字典项
            for (int i = 0; i < rowLen; i++) {
                Row row = dictSheet.getRow(i);
                if (row == null) {
                    row = dictSheet.createRow(i);
                }
                row.createCell(entry.getKey()).setCellValue(entry.getValue().get(i));
            }
            String excelColumn = getExcelColumn(entry.getKey());
            // 下拉框数据来源 eg:字典sheet!$B1:$B2
            String refers = dictSheetName + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen;
            // 创建可被其他单元格引用的名称
            Name name = workbook.createName();
            // 设置名称的名字
            name.setNameName("dict" + entry.getKey());
            // 设置公式
            name.setRefersToFormula(refers);

            /*** 设置下拉框数据 **/
            //DataValidationConstraint constraint = helper.createExplicitListConstraint((String[]) entry.getValue().toArray());
            DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + entry.getKey());
            DataValidation dataValidation = helper.createValidation(constraint, addressList);
            /*** 处理Excel兼容性问题 **/
            if (dataValidation instanceof HSSFDataValidation) {
                dataValidation.setSuppressDropDownArrow(false);
            } else {
                dataValidation.setSuppressDropDownArrow(true);
                dataValidation.setShowErrorBox(true);
            }

            //阻止输入非下拉框的值
            dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            dataValidation.createErrorBox("提示", "此值与单元格定义格式不一致!");

            sheet.addValidationData(dataValidation);
        }

    }

    /**
     * 将数字列转化成为字母列
     * 主要作用在于根据传入的列获取数据字典sheet中对应列
     * @param num
     * @return
     */
    private String getExcelColumn(int num) {
        String column = "";
        int len = alphabet.length - 1;
        int first = num / len;
        int second = num % len;
        if (num <= len) {
            column = alphabet[num] + "";
        } else {
            column = alphabet[first - 1] + "";
            if (second == 0) {
                column = column + alphabet[len] + "";
            } else {
                column = column + alphabet[second - 1] + "";
            }
        }
        return column;
    }
}
 
5. FileUtil工具类
 
public final class FileUtil {

    public static InputStream getResourcesFileInputStream(String fileName){
        return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);
    }

    public static String getPath(){
        return FileUtil.class.getResource("/").getPath();
    }

    public static File createNewFile(String pathName){
        File file = new File(getPath() + pathName);
        if(file.exists()){
            file.delete();
        }else{
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
        }
        return file;
    }

    public static File readFile(String pathName){
        return new File(getPath() + pathName);
    }

}


通过以上流程,即可实现本文开端描述的需求,如果对您有所帮助,可以点赞收藏哦~