SpringBoot整合POI导出通用Excel的方法示例

2020-08-24,,

一、准备工作

1、pom依赖

在pom.xml中加入POI的依赖

<dependency>
 <groupId>org.apache.poi</groupId>
 <artifactId>poi-ooxml</artifactId>
 <version>3.11-beta1</version>
</dependency>
<dependency>
 <groupId>org.apache.poi</groupId>
 <artifactId>poi-ooxml-schemas</artifactId>
 <version>3.11-beta1</version>
</dependency>

2、自定义注解

自定义注解,用于定义excel单元格的相关信息,用在需要导出的类上。

大家可以根据自己的实际需求来定义更多的内容。

@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelResources {
 
 int order() default 9999;//定义字段在excel的单元格列坐标位置

 String title() default "";//定义列坐标对应的标题

 int cloumn() default 100;//定义列宽

 String pattern() default "";//定义日期显示格式

}

3、定义需要导出的实体

举例说明@ExcelResources 的应用场景,我们创建一个demoModel,包含姓名、年龄、性别、日期。

后边的excel导出例子也采用这个实体类来举例。

@Data
public class ExcelDemoModel {

  @ExcelResources(order=0,title = "姓名",cloumn = 10)
  private String name;

  @ExcelResources(order=1,title = "年龄",cloumn = 10)
  private Integer age;

  @ExcelResources(order=2,title = "创建时间",cloumn = 24,pattern = "yyyy-MM-dd HH:mm:ss")
  private Date createTime;

  @ExcelResources(order=3,title = "性别",cloumn = 10)
  private SexType sex;//枚举
  
}

4、定义导出辅助类

用于存放导出的excel对应标题和列宽

@Data
@NoArgsConstructor
@AllArgsConstructor
public class TitleAndCloumn {

  private String title;//标题
  private int cloumn;//列宽

}

二、具体的导出方法

1、导出主要方法

@Service
public class ExcelService {

  private static float title_row_height=30;//标题行高
  private static float data_row_height=25;//数据行高

 public void exportExcel(HttpServletRequest request, HttpServletResponse response, String fileName ,List<?> excelDatas,Class<?> clz ) {
 
    try {

      HSSFWorkbook resultWb=new HSSFWorkbook();
      HSSFSheet sheet=resultWb.createSheet();//创建sheet

  //根据类类型信息获取导出的excel对应的标题和列宽 key-列号,value-标题和列宽
      HashMap<Integer, TitleAndCloumn> orderTitleAndCloumnMap=getTitleAndCloumnMap(clz);

      //设置列宽
      orderTitleAndCloumnMap.forEach((k,v) -> {
        sheet.setColumnWidth(k, v.getCloumn()*256);
      });

      HSSFRow row0=sheet.createRow(0);
      //设置标题行高
      row0.setHeightInPoints(title_row_height);

  //创建标题单元格格式
      HSSFCellStyle titleCellStyle=getCellStyle(resultWb,11,true,HSSFColor.BLACK.index);
      //填充标题行内容
      orderTitleAndCloumnMap.forEach((k,v) -> {
        HSSFCell row0Cell=row0.createCell(k);
        row0Cell.setCellValue(v.getTitle());
        row0Cell.setCellStyle(titleCellStyle);
      });

  //创建正文单元格格式
      HSSFCellStyle dataStyle = getCellStyle(resultWb,11,false,HSSFColor.BLACK.index);

  //将正文转换为excel数据
      int rowNum=1;
      for(Object data:excelDatas){

        HSSFRow row=sheet.createRow(rowNum++);
        row.setHeightInPoints(data_row_height);
  //获取对象值 key-列号 value-String值
        HashMap<Integer,String> orderValueMap=getValueMap(data);
        orderValueMap.forEach((k,v) ->{
          HSSFCell cell=row.createCell(k);
          cell.setCellValue(v);
          cell.setCellStyle(dataStyle);
            }
        );
      }

      String downFileName=fileName+".xls";
      response.setContentType("application/vnd.ms-excel; charset=UTF-8");// application/x-download
      response.setHeader("Content-Disposition", "attachment; "
          +encodeFileName(request, downFileName));

      OutputStream outputStream = response.getOutputStream();
      resultWb.write(outputStream);
      outputStream.flush();
      outputStream.close();
      resultWb.close();

    }catch (Exception e1) {
      e1.printStackTrace();
    }

  }
}

2、通过反射获取excel标题和列宽

/**
   * 获取类的属性对应单元格标题和列宽
   * @param
   * @return
   */
  private static HashMap<Integer, TitleAndCloumn> getTitleAndCloumnMap(Class<?> clz) {

    HashMap<Integer, TitleAndCloumn> orderTitleAndCloumnMap=new HashMap<>();

    Field[] fs = clz.getDeclaredFields();
    for(Field f:fs) {
      f.setAccessible(true);
      if(f.isAnnotationPresent(ExcelResources.class)) {
        Integer order=f.getAnnotation(ExcelResources.class).order();
        String title=f.getAnnotation(ExcelResources.class).title();
        int cloumn=f.getAnnotation(ExcelResources.class).cloumn();

        TitleAndCloumn titleAndCloumn=new TitleAndCloumn(title,cloumn);
        orderTitleAndCloumnMap.put(order,titleAndCloumn);
      }
    }

    return orderTitleAndCloumnMap;

  }

3、创建CellStyle

通过传入参数定义简单地CellStyle

public HSSFCellStyle getCellStyle(HSSFWorkbook workbook,int fontSize,boolean isBoleaWeight,short color){

    HSSFCellStyle style = workbook.createCellStyle();
    style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平居中
    style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中

    style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
    style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
    style.setBorderRight(HSSFCellStyle.BORDER_THIN);
    style.setBorderTop(HSSFCellStyle.BORDER_THIN);

    HSSFFont font = workbook.createFont();
    font.setFontHeightInPoints((short) fontSize);//字号
    font.setColor(color);//颜色
    font.setFontName("宋体");//字体

    if(isBoleaWeight){
      font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //字体加粗
    }

    style.setWrapText(true);
    style.setFont(font);

    return style;

  }

4、通过反射获取对象信息并处理成String字符串

我这里只涉及到基本数据类型和Date以及枚举的值获取和转换,小伙伴可以根据自己的实际情况进行修改。

/**
   * 获取对象的属性对应单元格坐标和值的键值对
   * @param obj
   * @return
   */
  private static HashMap<Integer, String> getValueMap(Object obj) throws IllegalAccessException {

    HashMap<Integer, String> result=new HashMap<>();

    Class<?> clz=obj.getClass();
    Field[] fs = clz.getDeclaredFields();
    for(Field f:fs) {
      f.setAccessible(true);
      if(f.isAnnotationPresent(ExcelResources.class)) {
        Integer order=f.getAnnotation(ExcelResources.class).order();
        String value="";

        Object valueObj=f.get(obj);
        if(valueObj!=null) {
   //日期格式进行特殊处理
          if(f.getType()==Date.class){

            String pattern=f.getAnnotation(ExcelResources.class).pattern();
            if(StringUtils.isEmpty(pattern)){
              pattern="yyyy-MM-dd HH:mm:ss";
            }
            SimpleDateFormat sdf=new SimpleDateFormat(pattern);
            value=sdf.format(valueObj);
          }else{
            value=valueObj.toString();//其他格式调用toString方法,这里枚举就需要定义自己的toString方法
          }

        }

        result.put(order, value);

      }
    }

    return result;
  }

5、枚举的定义

如果有用到枚举存储在数据库的小伙伴,可以自定义枚举的toString方法来实现excel导出时候相应的内容

public enum SexType {
 
 male("男"),
 female("女"),
 ;

 private String typeName;

 SexType(String typeName) {
 this.typeName = typeName;
 }

 @Override
 public String toString() {
 return typeName;
 }

}

6、encodeFileName

 /**
   * 根据不同的浏览器生成不同类型中文文件名编码
   *
   * @param request
   * @param fileName
   * @return
   * @throws UnsupportedEncodingException
   */
  public static String encodeFileName(HttpServletRequest request, String fileName)
      throws UnsupportedEncodingException
  {

    String new_filename = URLEncoder.encode(fileName, "UTF8").replaceAll("\\+", "%20");

    String agent = request.getHeader("USER-AGENT").toLowerCase();
    if (null != agent && -1 != agent.indexOf("msie"))
    {
      /**
       * IE浏览器,只能采用URLEncoder编码
       */
      return "filename=\"" + new_filename +"\"";
    }else if (null != agent && -1 != agent.indexOf("applewebkit")){
      /**
       * Chrome浏览器,只能采用ISO编码的中文输出
       */
      return "filename=\"" + new String(fileName.getBytes("UTF-8"),"ISO8859-1") +"\"";
    } else if (null != agent && -1 != agent.indexOf("opera")){
      /**
       * Opera浏览器只可以使用filename*的中文输出
       * RFC2231规定的标准
       */
      return "filename*=" + new_filename ;
    }else if (null != agent && -1 != agent.indexOf("safari")){
      /**
       * Safani浏览器,只能采用iso编码的中文输出
       */
      return "filename=\"" + new String(fileName.getBytes("UTF-8"),"ISO8859-1") +"\"";
    }else if (null != agent && -1 != agent.indexOf("firefox"))
    {
      /**
       * Firfox浏览器,可以使用filename*的中文输出
       * RFC2231规定的标准
       */
      return "filename*=" + new_filename ;
    } else
    {
      return "filename=\"" + new_filename +"\"";
    }
  }

三、方法调用案例

1、方法调用

public void exportExcelDemo(HttpServletRequest request, HttpServletResponse response) {

  //一系列查询处理
    List<ExcelDemoModel> demoList=new ArrayList<>();
    
    excelService.exportExcel(request,response,"人员信息demo",demoList,ExcelDemoModel.class);

  }

2、导出效果

到此这篇关于SpringBoot整合POI导出通用Excel的方法示例的文章就介绍到这了,更多相关SpringBoot整合POI导出Excel内容请搜索北冥有鱼以前的文章或继续浏览下面的相关文章希望大家以后多多支持北冥有鱼!

《SpringBoot整合POI导出通用Excel的方法示例.doc》

下载本文的Word格式文档,以方便收藏与打印。