自定义列Excel导出

自定义列Excel导出

前言: Excel导入导出是再常见不过的业务需求了,三年前刚入行就做过,当时查了不少百度、废了不少脑细胞才搞出来;这两天又遇到了同样的需求发现自己又忘了咋做,鼓捣出来后特此记录一下,以免又忘了咋做。

需求: 自定义列导出Excel,参数为 [{"prop":"属性名", "label":"列名"},{"prop":"属性名", "label":"列名"}...] 的JsonArrary的格式。后台逻辑会根据参数中的属性名从Bean中获取数据,然后写入到对应列名的Excel中。

Pom依赖

<!-- start 处理Excel依赖 -->
       <dependency>
           <groupId>xerces</groupId>
           <artifactId>xercesImpl</artifactId>
           <version>2.12.1</version>
       </dependency>
       <dependency>
           <groupId>org.apache.poi</groupId>
           <artifactId>poi</artifactId>
           <version>5.0.0</version>
       </dependency>
       <dependency>
           <groupId>org.apache.poi</groupId>
           <artifactId>poi-ooxml</artifactId>
           <version>5.0.0</version>
       </dependency>
       <dependency>
           <groupId>org.apache.poi</groupId>
           <artifactId>poi-ooxml-schemas</artifactId>
           <version>4.1.2</version>
       </dependency>
       <!-- end 处理Excel依赖 -->
   	<!-- fastjson-->
        <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.47</version>
       </dependency>
       <!-- hutool -->
       <dependency>
           <groupId>cn.hutool</groupId>
           <artifactId>hutool-all</artifactId>
           <version>5.7.2</version>
       </dependency>

导出Excel的工具类

package com.***.***.***;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class ExportExcel {

    // 导出表的标题
    private String title;
    // 导出表的列名
    private String[] rowName;
    // 导出表的数据
    private List<Object[]> dataList = new ArrayList<Object[]>();

    // 构造函数,传入要导出的数据
    public ExportExcel(String title, String[] rowName, List<Object[]> dataList) {
        this.dataList = dataList;
        this.rowName = rowName;
        this.title = title;
    }

    // 导出数据
    public void export(OutputStream out) throws Exception {
        try {
            //创建一个Excel并设置名称
            HSSFWorkbook workbook = new HSSFWorkbook();
            HSSFSheet sheet = workbook.createSheet(title);

            //生成表格标题行
            HSSFRow rowm = sheet.createRow(0);
            HSSFCell cellTitle = rowm.createCell(0);

            //定义SHEET样式
            HSSFCellStyle columnTopStyle = this.getColumnTopStyle(workbook);
            HSSFCellStyle style = this.getStyle(workbook);
            sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, (rowName.length - 1)));
            cellTitle.setCellStyle(columnTopStyle);
            cellTitle.setCellValue(title);

            //定义所需要的列数
            int columnNum = rowName.length;
            HSSFRow rowRowName = sheet.createRow(2);

            //将列头设置到SHEET的单元格中
            for (int n = 0; n < columnNum; n++) {
                HSSFCell cellRowName = rowRowName.createCell(n);
                cellRowName.setCellType(CellType.STRING);
                HSSFRichTextString text = new HSSFRichTextString(rowName[n]);
                cellRowName.setCellValue(text);
                cellRowName.setCellStyle(style);
            }

            //将数据设置到SHEET的单元格中
            for (int i = 0; i < dataList.size(); i++) {
                Object[] obj = dataList.get(i);
                HSSFRow row = sheet.createRow(i + 3);
                for (int j = 0; j < obj.length; j++) {
                    HSSFCell cell = null;
                    if (j == 0) {
                        cell = row.createCell(j, CellType.NUMERIC);
                        cell.setCellValue(i + 1);
                    } else {
                        cell = row.createCell(j, CellType.STRING);
                        if (!"".equals(obj[j]) && obj[j] != null) {
                            cell.setCellValue(obj[j].toString());
                        } else {
                            cell.setCellValue(" ");
                        }
                    }
                    cell.setCellStyle(style);
                }
            }

            //让列宽随着导出的列长自动适应
            for (int colNum = 0; colNum < columnNum; colNum++) {
                int columnWidth = sheet.getColumnWidth(colNum) / 256;
                for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
                    HSSFRow currentRow;
                    if (sheet.getRow(rowNum) == null) {
                        currentRow = sheet.createRow(rowNum);
                    } else {
                        currentRow = sheet.getRow(rowNum);
                    }
                    if (currentRow.getCell(colNum) != null) {
                        HSSFCell currentCell = currentRow.getCell(colNum);
                        if (CellType.STRING.getCode() == currentCell.getCellType().getCode()) {
                            int length = currentCell.getStringCellValue().getBytes().length;
                            if (columnWidth < length) {
                                columnWidth = length;
                            }
                        }
                    }
                }
                if (colNum == 0) {
                    sheet.setColumnWidth(colNum, (columnWidth - 2) * 256);
                } else {
                    sheet.setColumnWidth(colNum, (columnWidth + 4) * 256);
                }
            }
            if (workbook != null) {
                try {
                    workbook.write(out);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 表格标题样式
     */
    public HSSFCellStyle getColumnTopStyle(HSSFWorkbook workbook) {
        // 设置字体
        HSSFFont font = workbook.createFont();
        // 设置字体大小
        font.setFontHeightInPoints((short) 11);
        // 设置字体颜色
        font.setColor(IndexedColors.WHITE.getIndex());
        // 字体加粗
        font.setBold(true);
        // 设置字体名字
        font.setFontName("Courier New");
        // 设置样式
        HSSFCellStyle style = workbook.createCellStyle();
        // 设置标题背景色
        style.setFillForegroundColor(IndexedColors.DARK_TEAL.getIndex());
        // 设置背景颜色填充样式
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 设置低边框
        style.setBorderBottom(BorderStyle.THIN);
        // 设置低边框颜色
        style.setBottomBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 设置右边框
        style.setBorderRight(BorderStyle.THIN);
        // 设置顶边框
        style.setTopBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 设置顶边框颜色
        style.setTopBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 在样式中应用设置的字体
        style.setFont(font);
        // 设置自动换行
        style.setWrapText(false);
        // 设置水平对齐的样式为居中对齐;
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }

    /**
     * 表格数据样式
     */
    public HSSFCellStyle getStyle(HSSFWorkbook workbook) {
        // 设置字体
        HSSFFont font = workbook.createFont();
        // 设置字体大小
        font.setFontHeightInPoints((short) 10);
        // 设置字体名字
        font.setFontName("Courier New");
        // 设置样式;
        HSSFCellStyle style = workbook.createCellStyle();
        // 设置底边框;
        style.setBorderBottom(BorderStyle.THIN);
        // 设置底边框颜色;
        style.setBottomBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 设置左边框;
        style.setBorderLeft(BorderStyle.THIN);
        // 设置左边框颜色;
        style.setLeftBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 设置右边框;
        style.setBorderRight(BorderStyle.THIN);
        // 设置右边框颜色;
        style.setRightBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 设置顶边框;
        style.setBorderTop(BorderStyle.THIN);
        // 设置顶边框颜色;
        style.setTopBorderColor(IndexedColors.ROYAL_BLUE.getIndex());
        // 在样式用应用设置的字体;
        style.setFont(font);
        // 设置自动换行;
        style.setWrapText(false);
        // 设置水平对齐的样式为居中对齐;
        style.setAlignment(HorizontalAlignment.CENTER);
        // 设置垂直对齐的样式为居中对齐;
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }
}

反射调用get方法获取get方法结果

package com.***.***.***;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectUtils {


   /**
    * 拼接属性,调用目标Class的get方法
    *
    * @param c         属性所属实体类
    * @param filedName 需要调用的属性名
    * @param obj       实体类实例
    * @return 返回get方法结果
    */
   public static Object invokeGetMethod(Class<?> c, String filedName, Object obj) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
       String methodName = "get" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
       Class<?>[] nullClasses = null;
       Method method = c.getMethod(methodName, nullClasses);
       Object[] nullObjects = null;
       return method.invoke(obj, nullObjects);
   }
}

接口层

/**
  * @ClassName ********Controller
  * @MethodName export******Info
  * @Parm [columnTitle, response]
  * @Return com***.***.***.ResultResponse
  * @Company **********有限公司
  * @Dept 研发部
  * @Author 曹丞相
  * @Date 1145/11/4 19:19
  * @Version 1.0
  * @Description 导出*******信息接口
  */
 @ApiOperation("导出*****信息")
 @PostMapping(value = "/export******Info")
 @ApiImplicitParams({
         @ApiImplicitParam(name = "columnTitle", value = "导出列,示例:[{\"prop\":\"id\",\"label\":\"主键\"},{\"prop\":\"name\",\"label\":\"名称\"},{\"prop\":\"address\",\"label\":\"地址\"}]") })
 public ResultResponse export******Info(@RequestParam(value = "columnTitle") String columnTitle, HttpServletResponse response) {
     try {
 		//Excel文件名
         String fileName = URLEncoder.encode("******信息数据-" + DateUtils.format(new Date(), "yyyyMMddHHmmss") + ".xls", "UTF-8");
         String headStr = "attachment; filename=" + fileName;
         response.setContentType("APPLICATION/OCTET-STREAM");
         response.setHeader("Content-Disposition", headStr + ";" + "filename*=utf-8''" + fileName);
         i******Service.export(response.getOutputStream(), map, columnTitle);
         return ResultResponse.success();
     } catch (Exception e) {
         e.printStackTrace();
         return ResultResponse.failure();
     }
 }

Service实现类

//部分引用的包
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import java.util.*;

 /**
   * @ClassName ******ServiceImpl
   * @MethodName export
   * @Parm [out 输出流, columnTitle导出列]
   * @Return void
   * @Company ********有限公司
   * @Dept 研发部
   * @Author 曹丞相
   * @Date 1145/11/4 19:19
   * @Version 1.0
   * @Description 导出*******信息
   */
  @Override
  public void export(OutputStream out, String columnTitle) throws IOException {
      try {
  		//获取要导出的Bean数据
          List<*****PO> *****POS = list();
  		//初始化一个空白的List存放处理完后的Excel数据列
          List<Object[]> dataList = new ArrayList<Object[]>();
          // 把需要展示的列json数据转为List,利用JSONArray中的parseObject方法并指定返回类型来解析json数组字符串
          List<Map<String, String>> cols = JSONArray.parseObject(columnTitle, List.class);
          // 设置Excel列标题
          String[] rowsName = new String[cols.size() + 1];
          rowsName[0] = "序号";
          for (int i = 0; i < cols.size(); i++) {
              rowsName[i + 1] = cols.get(i).get("label").toString();
          }
          // 设置Excel行数据
          for (*****PO *****PO: *****POS) {
              Object[] objs = new Object[cols.size() + 1];
              objs[0] = "";
              for (int j = 0; j < cols.size(); j++) {
  				//取出属性名
                  String prop = cols.get(j).get("prop").toString();
  				//根据属性名反射获取该属性的get方法结果
  				Object o = ReflectUtils.invokeGetMethod(****PO.class, prop, ****PO);
  				//非空卫语句
                  if (o != null && StrUtil.isNotBlank(o.toString())) {
                      if (prop.equals("createTime")) {
  					//如果该属性是时间,则进行时间格式处理
                          objs[j + 1] = DateUtil.format(DateUtil.parse(o.toString()), "yyyy/MM/dd");
                      } else if (prop.equals("isDelete")) {
  					//如果该属性是数字,则进行相应的文字替换
                          objs[j + 1] = NumberUtil.parseInt(o.toString()) == 0 ? "是" : "否";
                      } else {
                          objs[j + 1] = o;
                      }
                  } else {
                      objs[j + 1] = "";
                  }
              }
              dataList.add(objs);
          }
  		//Excel标题
          String title = "****信息数据";
  		//导出Excel
          ExportExcel ex = new ExportExcel(title, rowsName, dataList);
          ex.export(out);
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          out.flush();
          out.close();
      }
  }

遇到的问题

在做的时候遇到了无法通过反射调用get方法的问题,忘记后续怎么解决的了,等想起来了再后续补充😅...
原因出在@Accessor(chain = true)注解上;这个注解是lombok里的,方便链式调用set方法。

原因详情:就因为加了Lombok的@Accessors(chain = true),bean拷贝工具类不干活了

参考资料

LauSET的CSDN博客:Vue+SpringBoot导出Excel,自定义要导出的表格的列


标题:自定义列Excel导出
作者:AzumaTokaku
地址:https://www.azumatokaku.cc/articles/2021/07/03/1625292929334.html

    评论
    0 评论
avatar

取消