话不多说,直接上代码(编码环境:jdk1.8、springboot框架)
用到的依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency>
1、编写注解类 用于标识需要展示到excel中的字段
package com.sztf.shidaikeyi.annotation;
import java.lang.annotation.*;
/**
* 自定义注解类 用来标识excel要导出的字段
*/
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelTitle {
/**
* 表头信息
*/
String head() default "";
}
2、创建实体类并使用注解(我这里用到了lombok 所以没有写get、set)
/**
* 维修工单实体类
*
* @Author: ljp
* @CreateDate: 2021/1/26 17:17
*/
@Data
@TableName("repair_order")
public class RepairOrder extends BaseEntity {
private static final long serialVersionUID = 6251454355648214332L;
/**
* 主键
*/
@TableId("id")
private Long id;
/**
* 关联项目id
*/
private Long projectId;
//以下字段不存在数据库
/**
* 项目名称
*/
@ExcelTitle(head = "项目名称")
@TableField(exist = false)
private String projectName;
/**
* 关联设备名称
*/
@ExcelTitle(head = "关联设备名称")
@TableField(exist = false)
private String equipmentName;
//以上字段不存在数据库
/**
* 工段名称
*/
@ExcelTitle(head = "工段名称")
private String workSegment;
/**
* 关联设备id
*/
private Long equipmentId;
/**
* 原因
*/
@ExcelTitle(head = "原因")
private String reason;
/**
* 解决办法
*/
@ExcelTitle(head = "解决办法")
private String solution;
/**
* 执行人
*/
@ExcelTitle(head = "执行人")
private String executor;
/**
* 填写人
*/
@ExcelTitle(head = "填写人")
private String writer;
/**
* 填写时间
*/
@ExcelTitle(head = "填写时间")
private String writeTime;
/**
* 截止时间
*/
@ExcelTitle(head = "截止时间")
private String deadline;
/**
* 签名
*/
@ExcelTitle(head = "签名")
private String signature;
/**
* 备注
*/
@ExcelTitle(head = "备注")
private String remark;
}
3、工具类的编写:
package com.sztf.shidaikeyi.util;
import com.sztf.shidaikeyi.annotation.ExcelTitle;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* 导出excel工具类
* 暂时没有完善 实体类中的字段要按照到处顺序排列 暂时没有想到好的办法
*
* @Author: ljp
* @CreateDate: 2021/3/17 15:21
*/
public class ExcelUtil<T> {
/**
* sheet页名称
*/
public static final String SHEET_NAME = "sheet";
/**
* 导出excel
*
* @param sheetName sheet页名称
* @param dataList 数据集合
* @param response 返回对象
* @param fileName 文件名称
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IOException
*/
public void exportExcel(String sheetName, List<T> dataList, HttpServletResponse response, String fileName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
//没有数据 直接结束
if (dataList == null || dataList.size() == 0) return;
//生成一个表格
HSSFWorkbook workbook = new HSSFWorkbook();
//创建sheet页
HSSFSheet sheet = workbook.createSheet(sheetName);
//设置表格默认列宽度为20个字节
sheet.setDefaultColumnWidth((short) 20);
T data = dataList.get(0);
//通过反射 获取所有字段信息
Field[] fields = data.getClass().getDeclaredFields();
//记录所有表头
List<String> titleList = new ArrayList<>();
//需要展示的字段合集
List<Field> showFields = new ArrayList<>();
for (Field field : fields) {
//如果字段上没有注解 代表不需要导出
if (field.getAnnotations().length == 0) continue;
for (Annotation annotation : field.getAnnotations()) {
if (annotation instanceof ExcelTitle) {
ExcelTitle excelTitle = (ExcelTitle) annotation;
titleList.add(excelTitle.head());
showFields.add(field);
}
}
}
//如果在实体类中没有找到excelTile注解 则直接返回
if (titleList.size() == 0) return;
//创建第一行
HSSFRow row = sheet.createRow(0);
//填充表头
for (int i = 0; i < titleList.size(); i++) {
row.createCell(i).setCellValue(titleList.get(i));
}
Iterator<T> iterator = dataList.iterator();
//当前行数
int rowIndex = 0;
while (iterator.hasNext()) {
row = sheet.createRow(++rowIndex);
T t = iterator.next();
for (int i = 0; i < showFields.size(); i++) {
HSSFCell cell = row.createCell(i);
Field field = showFields.get(i);
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Class<?> clazz = t.getClass();
Method getMethod = clazz.getMethod(getMethodName);
Object value = getMethod.invoke(t);
String cellValue;
if (value == null) {
cellValue = "";
} else {
if (value instanceof Date) {
cellValue = DateUtil.dateToStr((Date) value, DateUtil.YYYY_MM_DD_HH_MM_SS);
} else {
//可能是integer、double、float、long、string
cellValue = String.valueOf(value);
}
}
cell.setCellValue(cellValue);
}
}
response.setContentType("application/vnd.ms-excel;charset=utf-8");
OutputStream os = response.getOutputStream();
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ";" + "filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));
workbook.write(os);
os.flush();
os.close();
}
}
4、调用测试
编写接口,这里我就贴上我实际的代码,后台如何查询是不体现的
@GetMapping("/downloadExcel")
public void downloadExcel(String startTime, String endTime, Long projectId, Integer pageNum, HttpServletRequest request, HttpServletResponse response) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
SysUser curUser = (SysUser) redisTemplate.opsForValue().get(SimplifyUtil.getToken(request));
IPage<RepairOrder> iPage = repairOrderService.myPage(new Page<>(pageNum == null ? 0 : pageNum, Constant.LIMIT),
SimplifyUtil.buildCommonVo(startTime, endTime, projectId, curUser, userProjectService));
//上面是自己的业务逻辑 主要是在这里进行调用
new ExcelUtil<RepairOrder>().exportExcel(ExcelUtil.SHEET_NAME, iPage.getRecords(), response, EXCEL_NAME);
}
现在有个点想实现,但是没找到思路:
现在有个缺陷,就是导出的顺序是按实体类中字段的顺序导出的,感觉不太方便。如果可以在注解中再加一个排序字段,可以根据注解类中的排序字段进行导出就完美了,欢迎大神指导!