美文网首页
2022-05-15_luckysheet导出xls文件(第三方

2022-05-15_luckysheet导出xls文件(第三方

作者: 微笑碧落 | 来源:发表于2022-06-04 10:46 被阅读0次

    2022-05-15_luckysheet导出xls文件(第三方版)

    0.前言

    • 目前通过服务器端poi框架(第三方版)的方式进行导出xls文件。
    • 核心思想就是读取数据库存取的luckysheet格式的表格数据,通过poi转换为xls文件。然后给浏览器下载。
    • 该框架实现了字体、边框、图片、行列冻结、样式
    • 因为新版本的luckysheet更改了这个接口。作者的地址见附录。

    1.导入依赖

    • 注意版本。。poiV4不能使用
    <!--poi-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-scratchpad</artifactId>
        <version>3.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.12</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>3.12</version>
    </dependency>
    

    2.复制class文件

    • 复制如下文件。

    • 复制过来后,idea会有报错。都是import错误。删掉这些报错的import错误。然后交给idea自动import即可。

    • 文件清单
      ExcelUtils.java
      

    3.增加自定义导出文件名功能

    • 原来程序定死了文件名为XXXX20201124.xlsx。

    • 这里修改了调用方法,增加了文件名传入参数

    • 定位ExcelUtils.java 178行。增加文件名传入参数。
      //public static void exportLuckySheetXlsx(String excelData,HttpServletRequest request, HttpServletResponse response)
      public static void exportLuckySheetXlsx(String excelData,HttpServletRequest request, HttpServletResponse response, String fileName)
      
    • 定位ExcelUtils.java 226行。使用传入的文件名。
      //disposition += new String(("XXXX20201124.xlsx").getBytes(), "ISO8859-1");
      disposition += new String((fileName+".xlsx").getBytes(), "ISO8859-1");
      
      定位ExcelUtils.java 229行。使用传入的文件名。
      //disposition += URLEncoder.encode("XXXX20201124.xlsx", "UTF-8");
      disposition += URLEncoder.encode(fileName+".xlsx", "UTF-8");
      

    4.bug处理

    4.1字符串无法转换为Interger错误

    • 可能是fastjson版本缘故,直接使用这个库,会提示字符串无法转换为Interger错误。

    • 定位ExcelUtils.java 676行,其他类似处理即可。
      //xssfCellStyle.setBorderLeft(bordMap.get((int) l.get("style"))); //左边框
      xssfCellStyle.setBorderLeft(bordMap.get(l.getInteger("style")));
      

    4.2 JSONArray无法转换为JSONObject错误

    • 错误原因是luckysheet更改了接口。。。dataVerification现在是一个JSONArray。。

    • 暂时注释掉。。待后面慢慢处理。

    • 定位ExcelUtils.java 188行。注释掉如下语句
      //JSONObject dataVerification = jsonObject.getJSONObject("dataVerification");
      

    4.3 一个见鬼的bug

    • 这个bug简直见鬼了。虽说后面解决了。

    • bug现象:有值的单元格导出的表格文件包含边框,没有值的单元格就没有边框。

    • bug原因:这个框架,处理有值的单元格之前有一个createCellStyle方法。没有值的单元格就不会调用这个方法。这个方法有一个新增单元格格式的方法:XSSFCellStyle cellStyle = wb.createCellStyle();

    • 没有值的单元格在设置边框的时候用如下方法获取单元格格式对象:xssfCellStyle = cell.getCellStyle()。猜测这个方法不生效。。因为都还没对这个单元格set单元格格式对象。

    • 定位ExcelUtils.java 708行。修改成如下:
      XSSFCellStyle xssfCellStyle;
      if (sheet.getRow(row_) == null){
          sheet.createRow(row_);
      }
      if (sheet.getRow(row_).getCell(col_) == null){
          sheet.getRow(row_).createCell(col_);
          xssfCellStyle = wb.createCellStyle();
      }else{
          xssfCellStyle = sheet.getRow(row_).getCell(col_).getCellStyle();
      }
      

    4.4 NPE异常

    • 错误提示:

    • java.lang.NullPointerException
        at com.glg.collsheet.exportXls.ExcelUtils.setCellValue(ExcelUtils.java:539)
        at com.glg.collsheet.exportXls.ExcelUtils.exportLuckySheetXlsx(ExcelUtils.java:215)
      
    • 这个主要是因为有一些空sheet,是没有celldata对象的。celldata对象用来放单元格的值

    • 定位ExcelUtils.java 186行。如下语句返回的是null
      JSONArray jsonObjectList = jsonObject.getJSONArray("celldata");
      定位ExcelUtils.java 2156行。如下语句就会触发NPE
      setCellValue(wb,sheet,jsonObjectList,columnlenObject,rowlenObject,defaultRowHeight,defaultColWidth);
      
    • 增加非空判断即可。
      定位ExcelUtils.java 215行。
      if (jsonObjectList != null){
        setCellValue(wb,sheet,jsonObjectList,columnlenObject,rowlenObject,defaultRowHeight,defaultColWidth);
      }   
      

    4.5 部分特殊单元格数值导出为空

    • 特殊单元格是指有换行值的单元格、部分合并的单元格、数值格式不一样的单元格等。

    • 之所以原本的代码不管用,主要原因是这部分单元格的数值的保存方法不一样

    • celldata[0].v.ct.s是一个数组。。里面的每个s[0].v是保存的数值,需要把这部分的数值合并起来。

    • [图片上传失败...(image-f43282-1654854873451)]

      //定位ExcelUtils.java第559行,添加如下特殊处理
      Object ct_ = jsonObjectValue.get("ct");
      if (ct_ != null && ct_ instanceof JSONObject){
          Object s_ = ((JSONObject) ct_).get("s");
          if (s_ != null && s_ instanceof JSONArray){
              for (int i = 0; i < ((JSONArray) s_).size(); i++) {
                  m += ((JSONObject) ((JSONArray) s_).get(i)).getString("v");
              }
          }
      }
      

    5.新增不同边框处理功能

    • luckysheet修改了边框的接口了。新luckysheet的边框和原本很大不一样了。。

    • 有个边框类型的属性。如全边框。半边框。中间边框等。这个框架没有区分这些属性。

    • 感觉我的方法不是很简洁。有一些重复的代码。期待有更简洁的方法。但是不管怎么样。测试通过了。

    • //修改ExcelUtils.java 706行。修改成如下:
      int rowBegin = rowList.getInteger(0);
      int rowEnd = rowList.getInteger(rowList.size() - 1);
      int colBegin = columnList.getInteger(0);
      int colEnd = columnList.getInteger(columnList.size() - 1);
      for (int row_ = rowBegin; row_ < rowEnd + 1; row_++) {
          for (int col_ = colBegin; col_ < colEnd + 1; col_++) {
              setCellBorder(rowBegin,rowEnd,colBegin,colEnd,row_,col_,sheet,style_,color,borderType,wb);
          }
      }
      
    • //新增setCellBorder方法如下:
      private static void setCellBorder(int rowBegin, int rowEnd, int colBegin, int colEnd,int row_,int col_,
                                            XSSFSheet sheet, int style_,XSSFColor color, String borderType,XSSFWorkbook wb){
              XSSFCellStyle xssfCellStyle;
              if (sheet.getRow(row_) == null){
                  sheet.createRow(row_);
              }
              if (sheet.getRow(row_).getCell(col_) == null){
                  sheet.getRow(row_).createCell(col_);
                  xssfCellStyle = wb.createCellStyle();
              }else{
                  xssfCellStyle = sheet.getRow(row_).getCell(col_).getCellStyle();
              }
      
              switch (borderType){
                  case "border-all"://全边框
                      xssfCellStyle.setBorderLeft(bordMap.get(style_)); //左边框
                      xssfCellStyle.setLeftBorderColor(color);//左边框颜色
                      xssfCellStyle.setBorderRight(bordMap.get(style_)); //右边框
                      xssfCellStyle.setRightBorderColor(color);//右边框颜色
                      xssfCellStyle.setBorderTop(bordMap.get(style_)); //顶部边框
                      xssfCellStyle.setTopBorderColor(color);//顶部边框颜色
                      xssfCellStyle.setBorderBottom(bordMap.get(style_)); //底部边框
                      xssfCellStyle.setBottomBorderColor(color);//底部边框颜色
                      break;
                  case "border-left"://左边框
                      if (col_ != colBegin){break;}
                      xssfCellStyle.setBorderLeft(bordMap.get(style_)); //左边框
                      xssfCellStyle.setLeftBorderColor(color);//左边框颜色
                      break;
                  case "border-right"://右边框
                      if (col_ != colEnd){break;}
                      xssfCellStyle.setBorderRight(bordMap.get(style_)); //右边框
                      xssfCellStyle.setRightBorderColor(color);//右边框颜色
                      break;
                  case "border-top"://上边框
                      if (row_ != rowBegin){break;}
                      xssfCellStyle.setBorderTop(bordMap.get(style_)); //顶部边框
                      xssfCellStyle.setTopBorderColor(color);//顶部边框颜色
                      break;
                  case "border-bottom"://下边框
                      if (row_ != rowEnd){break;}
                      xssfCellStyle.setBorderBottom(bordMap.get(style_)); //底部边框
                      xssfCellStyle.setBottomBorderColor(color);//底部边框颜色
                      break;
                  case "border-outside"://外部边框
                      if (col_ == colBegin){
                          xssfCellStyle.setBorderLeft(bordMap.get(style_)); //左边框
                          xssfCellStyle.setLeftBorderColor(color);//左边框颜色
                      }
                      if (col_ == colEnd){
                          xssfCellStyle.setBorderRight(bordMap.get(style_)); //左边框
                          xssfCellStyle.setRightBorderColor(color);//左边框颜色
                      }
                      if (row_ == rowBegin){
                          xssfCellStyle.setBorderTop(bordMap.get(style_)); //左边框
                          xssfCellStyle.setTopBorderColor(color);//左边框颜色
                      }
                      if (row_ == rowEnd){
                          xssfCellStyle.setBorderBottom(bordMap.get(style_)); //左边框
                          xssfCellStyle.setBottomBorderColor(color);//左边框颜色
                      }
                      break;
                  case "border-inside"://内侧边框
                      if (col_ != colBegin){
                          xssfCellStyle.setBorderLeft(bordMap.get(style_)); //左边框
                          xssfCellStyle.setLeftBorderColor(color);//左边框颜色
                      }
                      if (col_ != colEnd){
                          xssfCellStyle.setBorderRight(bordMap.get(style_)); //左边框
                          xssfCellStyle.setRightBorderColor(color);//左边框颜色
                      }
                      if (row_ != rowBegin){
                          xssfCellStyle.setBorderTop(bordMap.get(style_)); //左边框
                          xssfCellStyle.setTopBorderColor(color);//左边框颜色
                      }
                      if (row_ != rowEnd){
                          xssfCellStyle.setBorderBottom(bordMap.get(style_)); //左边框
                          xssfCellStyle.setBottomBorderColor(color);//左边框颜色
                      }
                      break;
                  case "border-horizontal"://内侧水平边框
                      if (row_ == rowEnd || row_ == rowBegin){break;}
                      xssfCellStyle.setBorderTop(bordMap.get(style_)); //顶部边框
                      xssfCellStyle.setTopBorderColor(color);//顶部边框颜色
                      xssfCellStyle.setBorderBottom(bordMap.get(style_)); //底部边框
                      xssfCellStyle.setBottomBorderColor(color);//底部边框颜色
                      break;
                  case "border-vertical": //内侧垂直边框
                      if (col_ == colEnd || col_ == colBegin){break;}
                      xssfCellStyle.setBorderLeft(bordMap.get(style_)); //左边框
                      xssfCellStyle.setLeftBorderColor(color);//左边框颜色
                      xssfCellStyle.setBorderRight(bordMap.get(style_)); //右边框
                      xssfCellStyle.setRightBorderColor(color);//右边框颜色
                      break;
              }
              XSSFCell cell = sheet.getRow(row_).getCell(col_);
              cell.setCellStyle(xssfCellStyle);
          }
      

    6.LuckysheetController

    • 注意如下的List<JSONObject> lists = jfGridFileGetService.getAllSheetByGridKey(gridKey);该方法是官方提供,用来下载文档的所有数据。
    • 注意如下的ExcelUtils.exportLuckySheetXlsx(exceldata,request,response,fileName);
    @Slf4j
    @RestController
    @RequestMapping("luckysheet")
    @Tag(name = "表格文档相关接口")
    public class LuckysheetController {
        @Autowired
        private JfGridFileGetService jfGridFileGetService;
    
        @Autowired
        private ProjectService projectService;
    
        /*
        * 以下是使用第三方框架导出。
        * https://blog.csdn.net/zzq10066/article/details/110424977。
        * 实现了字体、边框、图片、数据验证(部分)、行列冻结、样式
         */
        @Operation(summary = "下载项目对象的文档")
        @Parameters({@Parameter(name = "项目ID")})
        @GetMapping("download")
        public void download(HttpServletResponse response, HttpServletRequest request, long projectID) {
            Project project = projectService.getProjectByID(projectID);
            String gridKey = project.getGridKey();
            String fileName = DateUtil.today() + project.getName();
            List<JSONObject> lists = jfGridFileGetService.getAllSheetByGridKey(gridKey);
            //去除luckysheet中 &#xA 的换行
            String exceldata = JSON.toJSONString(lists).replace("&#xA;", "\\r\\n");
            ExcelUtils.exportLuckySheetXlsx(exceldata,request,response,fileName);
        }
    }
    

    7.前端使用

    • 注意要用浏览器来访问这个地址。用jquery.get()方法没办法直接弹出下载,

    • function saveToXlsFromService() {
          let downUrl = "luckysheet/download?projectID=" + project.id;
          window.location.href=downUrl;
      }
      

    附录

    1.Java poi导出LuckySheet在线表格

    相关文章

      网友评论

          本文标题:2022-05-15_luckysheet导出xls文件(第三方

          本文链接:https://www.haomeiwen.com/subject/cxrrmrtx.html