草庐IT

JAVA动态生成excel模板;列自定义下拉框赋值

博客小胡 2024-05-09 原文

哈喽,2023大家开工大吉啊!财源滚滚!

业务需求:需要生成excel模板,且对部分列设置下拉框,进行动态赋值,效果如下:


拿上图举例:针对省这一列,不是填写,而是选择数据,也就是说我们生成excel文件的时候需要把数据填充到下拉框的列中。

大体逻辑就是:java生成excel文件,在生成excel文件的时候将部分列是设置成下拉框,并赋值。

而在 Java 中,操作 excel 目前有两个主流框架,分别是:

apache 的 poi
Apache POI是基于DOM方式进行解析,将文件直接加载内存,速度较快,适合文件数据量不大的应用场景。它分别对不同格式的文件提供不同的文件解析:

    HSSF:  操作Microsoft Excel格式。
    XSSF:  操作Microsoft Excel  OOXML格式。

HSSF主要用于解析.xls格式的Excel文件,而XSSF主要用于解析.xlsx格式的Excel文件

Java Excel
Java Excel是一开放源码项目,通过它Java开发人员可以操作Excel文件(读取,创建,更新等)。

由于其小巧 易用的特点, 逐渐已经取代了 POI-excel的地位。

HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls;

XSSFWorkbook:是操作Excel2007的版本,扩展名是.xlsx。

对于不同版本的EXCEL文档要使用不同的工具类,如果使用错了,会提示如下错误信息:

org.apache.poi.openxml4j.exceptions.InvalidOperationExceptionorg.apache.poi.poifs.filesystem.OfficeXmlFileException

java生成excel文件逻辑:
1:创建excel
2:在excel中创建表格
3:在表格中输入列名
4:输入数据

代码如下

	 //导出标头信息
    List<String> headers = Arrays.asList("客户姓名", "客户联系方式", "服务对象", "联系方式", "性别", "出生日期(yyyy-MM-dd)", "身份证号", "关系", "健康状况", "省", "市", "区", "服务地址详情", "一级来源渠道", "二级来源渠道", "首洽日期(yyyy-MM-dd)");
    
   // 创建workbook;可以理解为excel
    HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
    
   //创建sheet:可以理解为在excel中创建表格
    HSSFSheet sheet = hssfWorkbook.createSheet();
    
   // 创建表头,供用户输入:可以理解为为表格输入列名
    initHeaders(hssfWorkbook, sheet, headers);
	
	//对性别做下拉处理;由标头可知性别位于第五列(下标从0开始,也就是4代表性别)
   String sexType = "sex";
    Map<String, String> sexMap = new HashMap<>();
    //key放性别的字段,value逗号拼接性别的所有值
    sexMap.put(sexType, "男,女,未知");
    // 1000表示该列填充1000行
    HSSFDataValidation sexValidation = ExcelUtil.createBox(sexType, sexMap, 1, 1000, 4, 4);
    if (sexValidation != null) {
        sheet.addValidationData(sexValidation);
    }

    String relationType = "relation";
    Map<String, String> relationTypeMap = new HashMap<>();
    relationTypeMap.put(relationType, relationName);
    HSSFDataValidation relationValidation = ExcelUtil.createBox(relationType, relationTypeMap, 1, 1000, 7, 7);
    if (relationValidation != null) {
        sheet.addValidationData(relationValidation);
    }

	//省市区三级联动处理
    SysCity cityParam = new SysCity();
    List<SysCity> araeList = sysCityMapper.selectSysCityList(cityParam);

    //所有省级信息:去除ID为36的请选择信息
    List<SysCity> proList = araeList.stream().filter(model -> "1".equals(model.getType().toString()) && !"36".equals(model.getId().toString())).collect(Collectors.toList());
    
    HSSFSheet mapSheet = hssfWorkbook.createSheet("MAP");
    hssfWorkbook.setSheetHidden(hssfWorkbook.getSheetIndex(mapSheet), false);
    //所有一级
    List<String> mapOneList = new ArrayList<String>();
    //市区关系
    Map<String, List<String>> map = new HashMap<String, List<String>>();


    proList.stream().forEach(model -> {
        mapOneList.add(model.getCityname());
        //获取对应市级名称信息
        List<String> cityNameList = araeList.stream().filter(as -> (as.getPid().toString()).equals(model.getId().toString())).map(ma -> ma.getCityname()).collect(Collectors.toList());
        map.put(model.getCityname(), cityNameList);
        //获取市级信息
        List<SysCity> cityList = araeList.stream().filter(as -> (as.getPid().toString()).equals(model.getId().toString())).collect(Collectors.toList());
        cityList.stream().forEach(city -> {
            //获取对应市级名称信息
            List<String> conturyNameList = araeList.stream().filter(as -> (as.getPid().toString()).equals(city.getId().toString())).map(ma -> ma.getCityname()).collect(Collectors.toList());
            map.put(city.getCityname(), conturyNameList);
        });


    });

    // 3.写入数据 将数据写入sheet中并做好关联关系
    writeData(hssfWorkbook, mapSheet, mapOneList, map);
    // 4.设置数据有效性
    setDataValid(hssfWorkbook, sheet, mapOneList, map, "1", "so");

    /**
     *
     * 渠道来源二级联动处理
     *
     */

    List<BaseSourceChannel> channelList = new BaseSourceChannel();
    //一级线索
    List<String> fatherMap = new ArrayList<String>();
    //二级线索
    Map<String, List<String>> sonMap = new HashMap<String, List<String>>();
    //一级线索
    List<BaseSourceChannel> parentSourceList = channelList.stream().filter(x -> "1".equals(x.getType())).collect(Collectors.toList());

    parentSourceList.stream().forEach(source -> {
        fatherMap.add(source.getSourceName());
        //一级对应的二级线索
        List<String> sonSourceList = channelList.stream().filter(x -> x.getPid().equals(source.getId())).map(y -> y.getSourceName()).collect(Collectors.toList());

        sonMap.put(source.getSourceName(), sonSourceList);

    });
    HSSFSheet sourceSheet = hssfWorkbook.createSheet("sourceMap");
    writeData(hssfWorkbook, sourceSheet, fatherMap, sonMap);
    setDataValid(hssfWorkbook, sheet, fatherMap, sonMap, "2", "source");


    String filename = UUID.randomUUID().toString() + "_测试模板.xlsx";

    String downloadPath = Global.getDownloadPath() + filename;
    File desc = new File(downloadPath);
    if (!desc.getParentFile().exists()) {
        desc.getParentFile().mkdirs();
    }
    FileOutputStream out = null;
    try {
        out = new FileOutputStream(downloadPath);
        hssfWorkbook.write(out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (out != null) {
            try {
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    return AjaxResult.success(filename);

好了,代码已经提供,有什么不明白的欢迎互相讨论。

createBox 方法如下

   /**
     *  excel导出,有码值的数据使用下拉框展示。
     * @param col             列名
     * @param boxMap          码值集合
     * @param firstRow        插入下拉框开始行号
     * @param lastRow         插入下拉框结束行号
     * @param firstCol        插入下拉框开始列号
     * @param lastCol         插入下拉框结束行号
     * @return
     */
    public static HSSFDataValidation createBox(String col, Map<String, String> boxMap, int firstRow, int lastRow, int firstCol, int lastCol) {
        HSSFDataValidation dataValidation = null;
        //查询码值表
        String cols = "";
        if(null != boxMap.get(col)) {
            cols = boxMap.get(col);
        }
        //设置下拉框
        if(cols.length() > 0 && null != cols) {
            String str[] = cols.split(",");
            //指定行,列为下拉框
            CellRangeAddressList cas = new CellRangeAddressList(firstRow , lastRow , firstCol , lastCol);
            //创建下拉数据列
            DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(str);
            //将下拉数据放入下拉框
            dataValidation = new HSSFDataValidation(cas, dvConstraint);
        }
        return dataValidation;
    }

生成页面表头方法:initHeaders

/**
 * 生成主页面表头
 *
 * @param wb
 * @param mainSheet
 * @param headers
 */
private void initHeaders(HSSFWorkbook wb, HSSFSheet mainSheet, List<String> headers) {
    //表头样式
    HSSFCellStyle style = wb.createCellStyle();
    style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
    //字体样式
    HSSFFont fontStyle = wb.createFont();
    fontStyle.setFontName("微软雅黑");
    fontStyle.setFontHeightInPoints((short) 12);
    style.setFont(fontStyle);
    //生成主内容
    HSSFRow rowFirst = mainSheet.createRow(0);//第一个sheet的第一行为标题
    mainSheet.createFreezePane(0, 1, 0, 1); //冻结第一行
    //写标题
    for (int i = 0; i < headers.size(); i++) {
        HSSFCell cell = rowFirst.createCell(i); //获取第一行的每个单元格
        mainSheet.setColumnWidth(i, 4000); //设置每列的列宽
        cell.setCellStyle(style); //加样式
        cell.setCellValue(headers.get(i)); //往单元格里写数据
    }
}

有关JAVA动态生成excel模板;列自定义下拉框赋值的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  5. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  7. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  8. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  9. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  10. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

随机推荐