1. 介绍

提供poi的excel操作:如excel读取和excel写入,支持数据上传或数据下载的场景操作

2. 使用

gradle中

    implementation('org.yunchen.gb:gb-plugin-poi:1.4.0.1')

3. 功能描述

两个辅助类,分别是ExcelReadBuilder和ExcelWriteBuilder ExcelReadBuilder使用示例:

new ExcelBuilder(2003,file.bytes).eachLine {
    println "First column on row ${row.rowNum} = "+row.getCell(0) +row.getCell(1)
    println cell(1)
}

ExcelWriteBuilder使用示例:

ExcelWriteBuilder workbook = new ExcelWriteBuilder(new File('c://b.xls')).workbook {
    sheet("Data") { // sheet1
      row(["Invoice Number", "Invoice Date", "Amount"])
      row(["100", Date.parse("yyyy-MM-dd", "2010-10-18"), 123.45])
      row(["600", Date.parse("yyyy-MM-dd", "2010-11-17"), 132.54])
      skipRow(7)
      row(['页脚'])
    }
    sheet("Summary") { // sheet2
      row(["Sheet: Summary"])
      skipRow(10)
      row(["Total", 123.45 + 132.54])
    }
    sheet("sheet1"){
       skipRow(10)
       row(["Total", 123.45 + 132.54])
    }
  }
  workbook.dwonload();

4. 使用word模板

4.1. word模板使用

WordOperateBuilder是用来进行word模板的操作的辅助类,支持对word中文字、表格、图表进行直观操作。

jdk1.8 使用 版本1.3.1.6, jdk11以上需要使用版本1.4.0.1

4.2. 使用示例:

以下示例演示了加载远程json数据转化为Map对象,对word模板中的文字替换,表格中的公式计算,及图表的操作 针对word中使用${} 进行标记的地方进行变量替换或计算 如: ${year}

        //使用Object进行json和对象的转换
        ObjectMapper objectMapper=new ObjectMapper()
        Map needMap=objectMapper.readValue(dataJson,Map)
        //此处使用本地Map演示
        Map valueMap=[year:2025,num1:999,worker4:'工作者',ending:'多谢!再见!',value1:0.66,tHeader:'标题','row1':'row1',row2:'row2',row3:'row3',row4:'row4',row5:'row5',row6:'row6',row7:'row7',row8:'row8',row9:'row9',row10:'row10']
        Binding binding = new Binding()
        valueMap.each{k,v->
            binding.setVariable(k,v)
        }
        GroovyShell groovyShell = new GroovyShell(binding)
        binding.setVariable("valueMap",valueMap)
        //获取resources下的word模板文件
        File tempFile=File.createTempFile("_tmp",".docx")
        File tempOutFile=File.createTempFile("_tmp",".docx")
        //开发模式或jar部署模式获取模板文件
        URL templateFileResource=ClassLoader.getResource("templates/tools/template1.docx")
        if(templateFileResource){
            tempFile.bytes=new File(templateFileResource.toURI()).bytes
        }else{
            //jar包模式下获取模板文件
            tempFile.bytes=this.class.classLoader.getResourceAsStream("templates/tools/template1.docx")?.bytes
        }
        //操作word
        WordOperateBuilder wordOperateBuilder=new WordOperateBuilder(tempFile)
        wordOperateBuilder.with {
            //替换段落中的变量
            document {XWPFDocument  document->
                //改文字
                eachParagraph { XWPFParagraph paragraph->
                    replaceParagraphText(valueMap)
                }
            }
            //修改表格中的变量
            eachTable { XWPFTable table->
                eachTableRow{ XWPFTableRow row->
                    eachTableCell{ XWPFTableCell cell->
                        if(cell.text.contains('${') && cell.text.contains('}')){
                            String evalStr=cell.text.replaceAll('\\$','').replaceAll('\\{','').replaceAll('\\}','')
                            cell.text = groovyShell.evaluate(evalStr)?.toString()
                        }
                    }
                }
            }
            //写入chart中的数据表,以第一个chart的修改示例
            editChart(0){ XWPFChart chart->
                chart.setTitleText('new title3')
                workbook{
                    sheet('Sheet1'){ org.apache.poi.ss.usermodel.Sheet sheet->
                        //修改excel数据源数据
                        // 示例计算表格中的一个公式
                        Cell cell=sheet.getRow(2).getCell(1)
                        if(cell.toString().contains('${') && cell.toString().contains('}')){
                            String evalStr=cell.toString().replaceAll('\\$','').replaceAll('\\{','').replaceAll('\\}','')
                            cell.setCellValue(groovyShell.evaluate(evalStr))
                        }
                        //另一种修改方式
                        skipRow(1)
                        row(['河北省', 0.99])
                        //修改chart 中的cache数据 (判断表格区域)
                        XDDFCategoryDataSource category = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(1,10,0,1))
                        XDDFChartData.Series series=chart.getChartSeries().get(0).getSeries(0)
                        series.replaceData(category, XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1,10,1,1)))
                        series.plot()
                    }
                }
            }
            //两种输出文件的方式
            tempOutFile.bytes=getBytes()
            saveAs(tempOutFile.path)
        }

4.3. 新建word内容

        WordOperateBuilder wordOperateBuilder=new WordOperateBuilder()
        wordOperateBuilder.with {
            document {XWPFDocument  document->
                createParagraph{XWPFParagraph paragraph->
                    createRun { XWPFRun run->
                        run.text='新建word文档'
                        run.fontFamily='微软雅黑'
                        run.fontSize=12
                        run.color='FF0000'
                        run.bold=false
                    }
                }
                 createTable(3,3){ XWPFTable table->
                     int rowNum=0;
                     eachTableRow{ XWPFTableRow row->
                         rowNum++
                         int columnNum=0
                         eachTableCell{ XWPFTableCell cell->
                             columnNum++
                             cell.text = rowNum*10+columnNum
                         }
                     }
                }
                createChart(15* Units.EMU_PER_CENTIMETER,5*Units.EMU_PER_CENTIMETER){ XWPFChart chart->
                    chart.titleText='aaa'
                    List categories=['2023-01-01','2023-01-02','2023-01-03','2023-01-04','2023-01-05','2023-01-06']
                    List HistogramvaluesA=[1.74,2.31,0.65,1.42,2.00,1.73]
                    List LinevaluesA=[1.74,2.31,0.65,1.42,2.00,1.73]
                    String categoryDataRange=chart.formatRange(new CellRangeAddress(1,categories.size(),0,0))
                    String valuesDataRangeA=chart.formatRange(new CellRangeAddress(1,categories.size(),1,1))

                    XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories.toArray() as String[], categoryDataRange, 1);
                    XDDFNumericalDataSource<Double> valuesDataA = XDDFDataSourcesFactory.fromArray(HistogramvaluesA.toArray() as Number[], valuesDataRangeA, 1);
                    //创建X轴
                    XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.TOP);
                    // 左Y轴
                    XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
                    // 左Y轴和X轴交叉点在X轴0点位置,在这里我直接注释掉了。
                  leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
                  leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
                    // 构建坐标轴
                    leftAxis.crossAxis(bottomAxis);
                    bottomAxis.crossAxis(leftAxis);
                    //设置柱状图Y轴名称,方位和坐标轴大小
                    leftAxis.setTitle("降雨量/mm");
                    leftAxis.setCrosses(AxisCrosses.MAX);
                    leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
                    // create series
                    bottomAxis.setMajorTickMark(AxisTickMark.NONE);//取消X轴的标刻度
                    //获取X轴 图表的基本配置都在这个对象里面里面
                    CTCatAx catAx = chart.getCTChart().getPlotArea().getCatAxArray(0);
                    CTSkip ctSkip = CTSkip.Factory.newInstance();
                    //设置显示间隔
                    ctSkip.setVal((int) Math.ceil(1));
                    catAx.setTickLblSkip(ctSkip);
                    //设置标签位置为最下
                    CTTickLblPos ctTickLblPos = CTTickLblPos.Factory.newInstance();
                    ctTickLblPos.setVal(STTickLblPos.LOW);
                    catAx.setTickLblPos(ctTickLblPos);
                    //获取Y轴 图表的基本配置都在这个对象里面里面
                    CTValAx catAy = chart.getCTChart().getPlotArea().getValAxArray(0);
                    CTScaling ctScaling ;
                    ctScaling = catAy.addNewScaling();
                    //设置柱状图Y轴坐标最大值
                    ctScaling.addNewMax().setVal(8);

                    ctScaling.addNewOrientation().setVal(STOrientation.MAX_MIN);
                    catAy.setScaling(ctScaling);
                    // 设置图表背后的网格线
                    CTLineProperties ctLine = catAy.addNewMajorGridlines().addNewSpPr().addNewLn();
                    ctLine.addNewPrstDash().setVal(STPresetLineDashVal.DASH);

                    //创建柱状图数据对象
                    XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
                    ((XDDFBarChartData) data).setBarDirection(BarDirection.COL);
                    //柱状图图例标题
                    XDDFChartData.Series series = data.addSeries(categoriesData, valuesDataA);
                    int column=0
                    XSSFSheet sheet=chart.getWorkbook().createSheet()
                    sheet.createRow(0).createCell(column).cellValue='new title'
                    CellReference cellReference=new CellReference(sheet.getSheetName(), 0, column, true, true);
                    series.setTitle("下雨量", cellReference);
                    chart.plot(data);
//-----------------------------------------折线图-------------------------------------------------
                    // 右Y轴
                    XDDFValueAxis rightAxis = chart.createValueAxis(AxisPosition.RIGHT);
                    // 右Y轴和X轴交叉点在X轴最大值位置
                    rightAxis.setCrosses(AxisCrosses.MIN);
                    rightAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
                    // 构建坐标轴
                    rightAxis.crossAxis(bottomAxis);
                    bottomAxis.crossAxis(rightAxis);
                    //设置折线图Y轴名称
                    rightAxis.setTitle("水位/m");
                    XDDFCategoryDataSource countries = XDDFDataSourcesFactory.fromArray(categories.toArray() as String[]);
                    //设置折线图Y轴坐标最大值
                    rightAxis.setMaximum(8);
                    //LINE:折线图,
                    data = chart.createData(ChartTypes.LINE, bottomAxis, rightAxis);
                    //加载折线图数据
                    XDDFNumericalDataSource<Double> area = XDDFDataSourcesFactory.fromArray(LinevaluesA.toArray() as Number[]);
                    //图表加载数据,折线1
                    XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(countries, area);
                    //折线图例标题
                    series1.setTitle("水位", null);
                    //直线
                    series1.setSmooth(true);
                    //设置标记大小
                    series1.setMarkerSize((short) 2);
                    //设置空数据显示间隙
                    CTDispBlanksAs disp = CTDispBlanksAs.Factory.newInstance();
                    disp.setVal(STDispBlanksAs.GAP);
                    chart.getCTChart().setDispBlanksAs(disp);
                    data.setVaryColors(false);
                    //绘制
                    chart.plot(data);
                    //设置图表图例
                    XDDFChartLegend legend = chart.getOrAddLegend();
                    legend.setPosition(LegendPosition.TOP);
                }
                //document.write(new FileOutputStream(new File("C:\\Users\\xiaopeng\\Desktop\\new.docx")))
                saveAs("C:\\Users\\xiaopeng\\Desktop\\new.docx")
                //toHtml('C:\\Users\\xiaopeng\\Desktop\\new.html')
                //toPdf("C:\\Users\\xiaopeng\\Desktop\\new.pdf")
                close()
            }
        }

4.4. 使用样例word文件内容更改

WordOperateBuilder使用示例:模板wordTemplate.docx

        new WordOperateBuilder("C:\\Users\\Administrator\\Desktop\\wordTemplate.docx").document { XWPFDocument document->
            //操作段落
            eachParagraph {XWPFParagraph paragraph->
                println paragraph.getText()
                //操作文字组合
                //替换 设置样式 包括字体大小、颜色和粗体属性
                eachRun{XWPFRun replaceRun->

                    if(replaceRun.text()=='大学'){
                       replaceRun.setText('n',0)
                       replaceRun.setFontSize(36)
                       replaceRun.setColor('FF0000')
                       replaceRun.setBold(true)
                    }
                }
                // 插入设置
                eachRun{
                   XWPFRun insertRun ->
                       if (insertRun.text() == 'auto'){
                           insertRun.setText("在此处插入一段文字")
                       }
               }
            }

            //操作表格
            eachTable{XWPFTable table->
                eachTableRow{XWPFTableRow row->
                    eachTableCell{XWPFTableCell cell->
                        println cell.getText()
                    }
                }
            }
            //修改表格
            eachTable { XWPFTable table ->
               table.
               table.addNewCol()//加新行,包含原样式
               //替换行对象,使用操作文字方法
               eachTableRow { XWPFTableRow row ->
                   row.addNewTableCell()//加新列,包含原样式
                   //替换列对象,使用操作文字方法
                   row.getCell(0).setText("al")//追加值,包含原样式
                   row.getCell(3).setText("tx")//追加值,包含原样式
               }
               table.addNewCol()//加新行,包含原样式
            }


            //读取chart中的数据表
            eachChart{XWPFChart chart->
                eachLineInSheet([sheet:'Sheet1',labels:true]){Row row->
                    println "First column on row ${row.rowNum} = "+row.getCell(0) +row.getCell(1)
                    println cell(1)
                }
            }
            //写入chart中的数据表
            eachChart{XWPFChart chart->
                chart.setTitleText('new title3')
                workbook{
                    sheet('Sheet1'){ org.apache.poi.ss.usermodel.Sheet sheet->
                        //修改excel数据源数据
                        skipRow(1)
                        row([null, 400d])
                        row([null, 400d])
                        row([null, 400d])
                        row([null, 400d])
                        //修改chart 中的cache数据
                        XDDFCategoryDataSource category = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(1,4,0,0))
                        getChartSeries(0){XDDFChartData chartSeries->
                            getSeries(0){XDDFChartData.Series series->
                                series.setTitle(sheet.getRow(0).getCell(1).richStringCellValue.toString(), new CellReference(sheet.sheetName, 0, 1, true, true));
                                series.replaceData(category, XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1,4,1,1)));
                                series.plot();
                                //修改fill颜色
                                changeSeriesFillColor(PresetColor.CHARTREUSE)
                            }
                        }
                    }
                }

            }
            saveAs("C:\\Users\\xiaopeng\\Desktop\\new.docx")
            //toHtml('C:\\Users\\xiaopeng\\Desktop\\new.html')
            //toPdf("C:\\Users\\xiaopeng\\Desktop\\new.pdf")
        }