java-poi操作Excel,实现二级级联动态生成

本文介绍了一个Java程序,该程序能够动态地为Excel工作簿生成带有数据验证下拉列表的模板。通过从数据库获取有害信息类型并填充到隐藏的工作表中,程序实现了下拉列表的选择项动态更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接贴代码:

package com.jinhui.reportingcentre.baseinfo.typecatalog.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.Name;
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.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.jinhui.core.BaseAction;
import com.jinhui.reportingcentre.Constants;
import com.jinhui.reportingcentre.admin.system.service.ISysParamService;
import com.jinhui.reportingcentre.baseinfo.typecatalog.domain.TypeCatalogBean;
import com.jinhui.reportingcentre.baseinfo.typecatalog.service.ITypeCatalogService;
import com.jinhui.reportingcentre.utils.DeleteFileUtil;
import com.jinhui.reportingcentre.utils.HttpSessionUtil;

/**
 * 有害信息类型维护
 * @author bin
 */
@Controller
@RequestMapping("/typecatalog")
public class TypeAction extends BaseAction {
    private static Logger logger = Logger.getLogger(TypeAction.class);
    @Autowired
    private ITypeCatalogService typeCatalogService;
    
    private String EXCEL_HIDE_SHEET_NAME = "excelhidesheetname";
    private String HIDE_SHEET_NAME_BIGTYPE = "bigTypeList"; 
    private HashMap map = new HashMap();//危害小类
    public static final String ERROR = "error";

    @Autowired
    private ISysParamService sysParamService; // 系统参数service
    
    /**
     * 修改excel导入模板
     */
    @SuppressWarnings("unchecked")
    @RequestMapping("/updateExcel")
    public String updateExcel(@RequestParam("type") String type,@RequestParam("reporttype") String reporttype,HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        PrintWriter pw = response.getWriter();
        
        boolean flag = false;
        try {
            flag = rewriteExcelTempAtEmp("temp/templates/no_excelDome_web.xls",
                            "temp/templates/匿名批量导入.xls", 3, "C");
            if(flag){
                pw.print("1");
            }else{
                pw.print("2");
            }
        } catch (Exception e) {
            e.printStackTrace();
            pw.print("2");
        } finally {
            pw.flush();
            pw.close();
            return null;
        }
    }

    //private  bigList;//危害大类
    /**
     * 动态生成危害大类的excel模板
     * 
     * @throws IOException
     */
    
    
    /**
     * 动态生成大类小类级联的excel模板
     * @param excelStart 未添加级联的原始Excel模板路径
     * @param excelStop    生成最终Excel的保存路径
     * @param RowNum 生成在Excel的第几列
     * @param Rowletter 生成在Excel的列名(A,B,C...)
     * @return
     * @throws IOException
     */
    private boolean rewriteExcelTempAtEmp(String excelStart, String excelStop,
            int RowNum, String Rowletter) throws IOException {
        boolean flag = false;
        Workbook workbook = null;
        try {
            String[] bigList = bigTypeList();
            // 读取默认模板Excel文件
            String path = HttpSessionUtil.getRootPath() + excelStart;
            workbook = readWorkBook(path);

            if (EXCEL_HIDE_SHEET_NAME.equals(workbook
                    .getSheetIndex(EXCEL_HIDE_SHEET_NAME))) {
                workbook.removeSheetAt(workbook
                        .getSheetIndex(EXCEL_HIDE_SHEET_NAME)); // 删除隐藏表
            }
            
            Sheet hideInfoSheet = workbook.createSheet(EXCEL_HIDE_SHEET_NAME);// 隐藏一些信息
            // 在隐藏页设置选择信息
            // 第二行设置危害大类
            Row bigNameRow = hideInfoSheet.createRow(0);
            creatRow(bigNameRow, bigList);
            // 以下行设置危害小类列表
            for (int i = 0; i < map.size(); i++) {
                Row smallNameRow = hideInfoSheet.createRow(i + 1);
                String[] Stringlist = (String[]) map.get("bigList" + i);
                creatRow(smallNameRow, Stringlist);
            }
            // 名称管理
            // 第一行设置大类信息
            creatExcelNameList(workbook, HIDE_SHEET_NAME_BIGTYPE, 1,
                    bigList.length, false);
            // 以后动态大小设置大类对应的小类列表

            for (int i = 0; i < bigList.length; i++) {
                String[] Stringlist = (String[]) map.get("bigList" + i);
                creatExcelNameList(workbook, bigList[i], i + 2,
                        Stringlist.length, true);
            }

            // 设置隐藏页标志
            workbook.setSheetHidden(
                    workbook.getSheetIndex(EXCEL_HIDE_SHEET_NAME), true);

            // 添加数据
            setDataValidation(workbook, RowNum, Rowletter);

            String newPath = HttpSessionUtil.getRootPath()+excelStop;

            /**** 删除旧的模板 ******/
            DeleteFileUtil.deleteFile(newPath);

            /**** 生成新的模板 ******/
            // 由于POI打开读取文件后再保存时bug问题, 只能重新定义一个新的Excel写入数据
            createExcel(workbook, newPath);
            
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
            workbook.close();
            flag = false;
        }
        return flag;
    }

    /**
     * 读取服务器上面的上传的excel文件
     * 
     * @param path
     * @return
     */
    public static Workbook readWorkBook(String path) {
        Workbook wb = null;
        try {
            wb = WorkbookFactory.create(new File(path));
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return wb;
    }

    /**
     * 创建一列数据
     * 
     * @param currentRow
     * @param textList
     */
    private static void creatRow(Row currentRow, String[] textList) {
        if (textList != null && textList.length > 0) {
            int i = 0;
            for (String cellValue : textList) {
                Cell userNameLableCell = currentRow.createCell(i++);
                userNameLableCell.setCellValue(cellValue);
            }
        }
    }

    /**
     * 添加数据验证选项
     * 
     * @param sheet
     */
    public void setDataValidation(Workbook wb, int RowNum, String Rowletter) {
        int sheetIndex = wb.getNumberOfSheets();
        if (sheetIndex > 0) {
            for (int i = 0; i < sheetIndex; i++) {
                Sheet sheet = wb.getSheetAt(i);
                if (!EXCEL_HIDE_SHEET_NAME.equals(sheet.getSheetName())) {
                    DataValidation data_validation_list = null;
                    // 危害大类选项添加验证数据
                    for (int a = 4; a < 581; a++) {
                        // data_validation_list =
                        // getDataValidationByFormula(HIDE_SHEET_NAME_BIGTYPE,a,8);
                        data_validation_list = getDataValidationByFormula(
                                HIDE_SHEET_NAME_BIGTYPE, a, RowNum);
                        sheet.addValidationData(data_validation_list);
                        // 危害小类选项添加验证数据
                        // data_validation_list =
                        // getDataValidationByFormula("INDIRECT(H"+a+")",a,9);
                        data_validation_list = getDataValidationByFormula(
                                "INDIRECT($" + Rowletter +"$"+ a + ")", a,
                                RowNum + 1);
                        sheet.addValidationData(data_validation_list);
                    }
                }
            }
        }
    }

    /**
     * 使用已定义的数据源方式设置一个数据验证
     * 
     * @param formulaString
     * @param naturalRowIndex
     * @param naturalColumnIndex
     * @return
     */
    private static DataValidation getDataValidationByFormula(
            String formulaString, int naturalRowIndex, int naturalColumnIndex) {
        // 加载下拉列表内容
        DVConstraint constraint = DVConstraint
                .createFormulaListConstraint(formulaString);
        // 设置数据有效性加载在哪个单元格上。
        // 四个参数分别是:起始行、终止行、起始列、终止列
        int firstRow = naturalRowIndex - 1;
        int lastRow = naturalRowIndex - 1;
        int firstCol = naturalColumnIndex - 1;
        int lastCol = naturalColumnIndex - 1;
        CellRangeAddressList regions = new CellRangeAddressList(firstRow,
                lastRow, firstCol, lastCol);
        // 数据有效性对象
        DataValidation data_validation_list = new HSSFDataValidation(regions,
                constraint);
        // //设置输入信息提示信息
        // data_validation_list.createPromptBox("下拉选择提示","请使用下拉方式选择合适的值!");
        // //设置输入错误提示信息
        // data_validation_list.createErrorBox("选择错误提示","你输入的值未在备选列表中,请下拉选择合适的值!");
        return data_validation_list;
    }

    /**
     * 创建一个名称
     * 
     * @param workbook
     */
    private void creatExcelNameList(Workbook workbook, String nameCode,
            int order, int size, boolean cascadeFlag) {
        Name name;
        name = workbook.createName();
        name.setNameName(nameCode);
        name.setRefersToFormula(EXCEL_HIDE_SHEET_NAME + "!"
                + creatExcelNameList(order, size, cascadeFlag));
    }

    /**
     * 名称数据行列计算表达式
     * 
     * @param workbook
     */
    private static String creatExcelNameList(int order, int size,
            boolean cascadeFlag) {
        char start = 'A';
        if (cascadeFlag) {
            start = 'B';
            if (size <= 25) {
                char end = (char) (start + size - 1);
                return "$" + start + "$" + order + ":$" + end + "$" + order;
            } else {
                char endPrefix = 'A';
                char endSuffix = 'A';
                if ((size - 25) / 26 == 0 || size == 51) {// 26-51之间,包括边界(仅两次字母表计算)
                    if ((size - 25) % 26 == 0) {// 边界值
                        endSuffix = (char) ('A' + 25);
                    } else {
                        endSuffix = (char) ('A' + (size - 25) % 26 - 1);
                    }
                } else {// 51以上
                    if ((size - 25) % 26 == 0) {
                        endSuffix = (char) ('A' + 25);
                        endPrefix = (char) (endPrefix + (size - 25) / 26 - 1);
                    } else {
                        endSuffix = (char) ('A' + (size - 25) % 26 - 1);
                        endPrefix = (char) (endPrefix + (size - 25) / 26);
                    }
                }
                return "$" + start + "$" + order + ":$" + endPrefix + endSuffix
                        + "$" + order;
            }
        } else {
            if (size <= 26) {
                char end = (char) (start + size - 1);
                return "$" + start + "$" + order + ":$" + end + "$" + order;
            } else {
                char endPrefix = 'A';
                char endSuffix = 'A';
                if (size % 26 == 0) {
                    endSuffix = (char) ('A' + 25);
                    if (size > 52 && size / 26 > 0) {
                        endPrefix = (char) (endPrefix + size / 26 - 2);
                    }
                } else {
                    endSuffix = (char) ('A' + size % 26 - 1);
                    if (size > 52 && size / 26 > 0) {
                        endPrefix = (char) (endPrefix + size / 26 - 1);
                    }
                }
                return "$" + start + "$" + order + ":$" + endPrefix + endSuffix
                        + "$" + order;
            }
        }
    }

    /**
     * 根据路径创建Excel
     * 
     * @author duanqm
     * @param workbook
     * @param path
     */
    public static void createExcel(Workbook workbook, String path) {
        System.setProperty("sun.jnu.encoding","utf-8");
        FileOutputStream fileOut = null;
        try {
            fileOut = new FileOutputStream(path);
            workbook.write(fileOut);
        } catch (Exception e) {
            // logger.error("Error create excel: ", e.getMessage());
        } finally {
            try {
                if (fileOut != null) {
                    fileOut.close();
                    workbook.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取所有可显示的危害大类,组成数组
     * 
     * @return
     */
    public String[] bigTypeList() {
        String[] List = new String[] {};

        TypeCatalogBean bigBean = new TypeCatalogBean();
        bigBean.setParentCodeId(Constants.CODEKIND_TYPECATALOG);
        bigBean.setIsdisplay(0);
        List<TypeCatalogBean> bigList = typeCatalogService.findNoNetwork(bigBean);

        //去掉侵权类0105
        for (int i = 0; i < bigList.size(); i++) {
            TypeCatalogBean bean = bigList.get(i);
            if (bean.getItemId().equals("0105")) {
                bigList.remove(bean);
                break;
            }
        }
        
        if (bigList.size() > 0) {
            String[] blist = new String[bigList.size()];
            for (int i = 0; i < bigList.size(); i++) {
                blist[i] = bigList.get(i).getItemName();// 危害大类存入数组

                // 获取子类
                TypeCatalogBean smallBean = new TypeCatalogBean();
                smallBean.setParentCodeId(bigList.get(i).getItemId());
                List<TypeCatalogBean> smallList = typeCatalogService
                        .queryForList(smallBean);

                if (smallList.size() > 0) {
                    // 存放子类
                    String[] slist = new String[smallList.size() + 1];
                    slist[0] = bigList.get(i).getItemName();// 危害大类先放进去
                    for (int j = 0; j < smallList.size(); j++) {
                        slist[j + 1] = smallList.get(j).getItemName();// 危害子类存入数组
                    }
                    map.put("bigList" + i, slist);
                } else {
                    String[] slist = new String[1];
                    slist[0] = bigList.get(i).getItemName();// 危害大类先放进去
                    map.put("bigList" + i, slist);
                }
            }
            List = blist;
        }
        return List;
    }
    
}
 

<think>嗯,用户想要用Java实现POI的四级联动下拉选,而且只要后端代码。首先,我得确认他指的是Apache POI库,用来处理Excel文件。四级联动下拉通常指的是在Excel中,四个下拉菜单,每个下拉的选项依赖于前一个的选择。比如,选择省份后,城市下拉显示该省的选项,依此类推。 用户可能需要生成一个包含四级联动数据验证的Excel文件。后端代码的话,可能需要用POI创建Workbook,设置数据验证规则。这时候需要考虑POI的版本,因为不同版本的API可能有差异。比如,XSSFWorkbook用于.xlsx格式,HSSFWorkbook用于旧的.xls格式,但现在一般用XSSF。 四级联动需要用到级联数据验证,POI本身可能不支持直接的级联,所以可能需要使用隐藏的工作表来存储各个别的选项,然后通过公式引用这些区域。比如,第一下拉直接引用一个区域,第二级下拉用INDIRECT函数根据第一的选择动获取对应的区域名称。 首先,需要创建四个别的数据源,可能用Map结构存储,比如省---街道的层。然后,在Excel中为每个别创建命名区域,名称可能需要与父选择的值相关联,比如“北京”对应“北京_市”。接着,在数据验证中设置公式,比如=INDIRECT(A1),假设A1是省的选择,那么市的下拉就会引用名为A1值的命名区域。 需要注意的是,Excel的名称不能有空格或特殊字符,可能需要处理一下键值,比如替换空格为下划线。另外,每个别的数据需要放在隐藏的工作表中,避免用户直接看到。 然后,代码步骤大概是:创建Workbook,创建隐藏的数据工作表,填充各个别的数据,创建命名区域,设置数据验证到目标单元格。最后,将Workbook写入输出流。 用户可能需要参考如何正确使用POI的DataValidationHelper和DataValidationConstraint,特别是公式类型的约束。同时,处理多级联动时,每个下的数据验证需要动引用上单元格的值,这可能需要用到INDIRECT函数和命名区域的组合。 可能的坑点包括命名区域的作用域、公式的正确性,以及数据填充时确保每个父对应的子数据正确。测试时需要检查生成Excel文件是否能够正确显示四下拉,并且选项之间正确联动。 总结下来,后端代码的结构是:准备数据,创建Excel结构,设置命名区域,应用数据验证。需要确保使用正确的POI类和方法,处理命名区域的创建,以及数据验证的公式设置。</think>以下是用Java POI实现级联动下拉选的后端代码实现思路和示例: 一、实现思路 1. 使用隐藏Sheet存储所有级联数据 2. 创建名称管理器定义数据范围 3. 通过数据验证设置下拉规则 4. 使用INDIRECT函数实现级联效果 二、核心代码实现(Spring Boot示例) ```java public class ExcelFourLevelDropDown { // 模拟数据源(实际应从数据库获取) private static final Map<String, Map<String, Map<String, List<String>>>> DATA_SOURCE = new HashMap<>(); static { Map<String, Map<String, List<String>>> cityData = new HashMap<>(); Map<String, List<String>> districtData = new HashMap<>(); List<String> streetData = Arrays.asList("街道A", "街道B"); districtData.put("区1", streetData); cityData.put("市1", districtData); DATA_SOURCE.put("省1", cityData); } public void createExcelWithFourLevelDropDown(HttpServletResponse response) throws IOException { try (XSSFWorkbook workbook = new XSSFWorkbook()) { // 创建隐藏数据Sheet XSSFSheet dataSheet = workbook.createSheet("data"); workbook.setSheetHidden(workbook.getSheetIndex(dataSheet), true); // 创建主Sheet XSSFSheet mainSheet = workbook.createSheet("Main"); // 设置四标题 Row headerRow = mainSheet.createRow(0); headerRow.createCell(0).setCellValue("省"); headerRow.createCell(1).setCellValue("市"); headerRow.createCell(2).setCellValue("区"); headerRow.createCell(3).setCellValue("街道"); // 生成数据验证 setupDataValidation(workbook, mainSheet); // 设置响应头 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment; filename=four_level.xlsx"); workbook.write(response.getOutputStream()); } } private void setupDataValidation(XSSFWorkbook workbook, XSSFSheet sheet) { // 第一(省) DataValidationHelper dvHelper = sheet.getDataValidationHelper(); DataValidationConstraint provinceConstraint = dvHelper.createExplicitListConstraint( DATA_SOURCE.keySet().toArray(new String[0]) ); CellRangeAddressList provinceRange = new CellRangeAddressList(1, 100, 0, 0); DataValidation provinceValidation = dvHelper.createValidation(provinceConstraint, provinceRange); sheet.addValidationData(provinceValidation); // 创建名称管理器实现级联 createNameRanges(workbook); // 第二级(市) DataValidationConstraint cityConstraint = dvHelper.createFormulaListConstraint("INDIRECT($A2)"); CellRangeAddressList cityRange = new CellRangeAddressList(1, 100, 1, 1); DataValidation cityValidation = dvHelper.createValidation(cityConstraint, cityRange); sheet.addValidationData(cityValidation); // 第三(区)和第四(街道)同理,使用INDIRECT函数级联 // ... } private void createNameRanges(XSSFWorkbook workbook) { XSSFSheet dataSheet = workbook.getSheet("data"); DATA_SOURCE.forEach((province, cities) -> { // 为每个省创建命名区域 int rowNum = 0; Row row = dataSheet.createRow(rowNum++); row.createCell(0).setCellValue(province); cities.keySet().forEach(city -> { row.createCell(1).setCellValue(city); // 为每个市创建命名区域(示例代码需扩展) // ... }); // 定义名称范围 Name name = workbook.createName(); name.setNameName(cleanName(province)); String formula = "data!$B$1:$B$" + cities.size(); name.setRefersToFormula(formula); }); } private String cleanName(String original) { return original.replaceAll("[^a-zA-Z0-9]", "_"); } } ``` 三、关键点说明 1. 数据存储: - 使用隐藏Sheet存储原始数据 - 使用Map结构维护四数据关系 2. 名称管理器: - 为每个别的选项创建命名区域 - 名称需要符合Excel命名规范(使用cleanName处理特殊字符) 3. 数据验证: - 第一使用显式列表 - 后续别使用INDIRECT函数引用上选择值 - 通过DataValidationHelper创建验证规则 4. 扩展建议: - 添加缓存机制提高性能 - 增加异常处理 - 根据实际数据量调整CellRangeAddressList范围 实际使用时需要: 1. 完善数据加载逻辑(建议数据库查询) 2. 补充区、街道别的数据验证代码 3. 根据业务需求调整验证范围 4. 处理特殊字符和命名冲突问题 建议配合前端使用以下技术: 1. EasyExcel(阿里开源的Excel处理工具) 2. 数据字典服务维护级联数据 3. 分布式缓存存储常用数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值