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

作者:mengdi_cao 时间:2021-12-30 21:13:12 

一、准备工作

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的方法示例

来源:https://blog.csdn.net/mengdi_cao/article/details/108143004

标签:SpringBoot,POI,Excel
0
投稿

猜你喜欢

  • Java字符串拼接新方法 StringJoiner用法详解

    2022-06-16 13:02:13
  • Spring一步到位精通拦截器

    2022-01-03 18:31:50
  • Java多线程状态及方法实例解析

    2021-09-10 22:49:53
  • Java多线程之synchronized关键字的使用

    2023-12-12 21:46:16
  • 解读JDK8踩坑JCE加密限制版本问题

    2021-09-20 12:42:35
  • SpringBoot下如何实现支付宝接口的使用

    2023-11-06 14:26:15
  • springboot集成shiro权限管理简单实现

    2023-10-27 13:39:02
  • SpringBoot整合ES解析搜索返回字段问题

    2023-01-26 21:39:53
  • MyBatis 如何获取子类的属性

    2022-09-12 14:42:39
  • java导出csv格式文件的方法

    2022-07-24 20:26:36
  • JDBC实现学生管理系统

    2023-01-22 13:22:19
  • springboot实现注册加密与登录解密功能(demo)

    2021-12-15 15:10:08
  • Android填坑系列:在小米系列等机型上放开定位权限后的定位请求弹框示例

    2022-03-29 15:15:49
  • SpringBoot防止大量请求攻击的实现

    2023-11-24 16:42:54
  • Groovy的规则脚本引擎实例解读

    2023-07-11 21:24:04
  • Java使用application.property读取文件里面的值

    2023-11-01 07:24:56
  • Java 中ThreadLocal类详解

    2022-01-31 19:58:17
  • Mybatis-Plus之ID自动增长的设置实现

    2022-10-27 00:09:47
  • mybatis-plus多表联查join的实现

    2023-11-24 06:49:56
  • Android Handler源码深入探究

    2023-02-17 11:54:53
  • asp之家 软件编程 m.aspxhome.com