SpringBoot整合POI导出通用Excel的方法示例
作者:mengdi_cao 发布时间:2021-12-30 21:13:12
标签:SpringBoot,POI,Excel
一、准备工作
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、导出效果
来源:https://blog.csdn.net/mengdi_cao/article/details/108143004


猜你喜欢
- 本文实例讲述了JAVA线程池原理。分享给大家供大家参考,具体如下:线程池的优点1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每
- 利用C#编写一个计算器。如下图,能够完成基本的四则运算。当然这个程序甚至还不上Windows附件那个自带的多功能计算器。 不过这个
- 本文实例为大家分享了Android创建发送和接收短信应用的简单实现过程,供大家参考,具体内容如下一、接收短信项目的结构如下:一个简单的接收和
- 1. 前言SpringBoot在包扫描时,并不会扫描子模块下的内容,这样就使得我们的子模块中的Bean无法注入到Spring容器中。Spri
- 一、前言在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试。这些都是通过对同一时刻请求数量
- Android USB转串口通信开发实例详解好久没有写文章了,年前公司新开了一个项目,是和usb转串口通信相关的,需求是用安卓平
- 有时间我们在使用多线程的时候,需要取消线程的执行,可以使用CancellationTokenSource来取消对Task开辟多线程的取消如下
- 本文实例为大家分享了Android Scroller的使用方法,供大家参考,具体内容如下1、scrollTo和ScrollByView类定义
- System.ComponentModel.Design.DesignSurface是为设计组件提供一个用户界面,通过它可以实现一个简单的窗
- 本文实例讲述了C#实现农历日历的方法。分享给大家供大家参考。具体实现方法如下://天干 private static
- 介绍和使用场景1)什么是消息一个事件,需要广播或者单独传递给某个接口2)为什么使用这个配置更新了,但是其他系统不知道是否更新SpringCl
- 一. 类型转型将一个类型转换成另一个类型的过程被称为类型转换。 我们所说的对象类型转换,一般是指两个存在继承关系的对象,而不是任意类型的对象
- asp.net页面中如何获取Excel表的内容,具体内容介绍如下所示:首先引用组件和命名空间using Microsoft.Office.I
- 前言我们知道在Android开发中不能在非ui线程中更新ui,但是,有的时候我们需要在代码中执行一些诸如访问网络、查询数据库等耗时操作,为了
- 1,什么是Eureka,什么是服务注册与发现 Spring Boot作为目前最火爆的web框架。那么它与Eureka又有什么关联呢?Eure
- 本文实例讲述了C#正则表达式匹配与替换字符串功能。分享给大家供大家参考,具体如下:事例一:\w+=>[A-Za-z1-9_],\s+=
- 大家都知道为了防止我们的网站被有些人和黑客恶意攻击,比如我们网站的注册页面,如果我们在用户注册的时候不加上一个验证码框的话,别人就可以写一个
- SpringBoot @ComponentScan的使用SpringBoot的启动类中有一个@ComponentScan,之前项目由于这个注
- 在很多项目中,为了安全安全考虑,需要对数据包进行加密处理,本文实例所述的即为C#加密代码,在应用开发中有很大的实用价值。说起数据包加密,其实
- C#中添加窗口的步骤:1是添加窗口。2是在程序中使用new实例化窗口类对象,并显示窗口。1 添加窗口在解决方案管理器->主项目名称-&