第一步添加依赖
<dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-spring-boot-starter</artifactId> <version>4.1.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-scratchpad --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-schemas --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> </dependency>
创建实体
DrillingDaily 注解@Excel表示需要导出那个字段,默认将其全部导出,不需要导出哪个列是,将注解
注解@Excel的值改为空字符串,就可以根据传入的表头信息灵活导出excel
package com.shiwen.jdzx.model.daily; import cn.afterturn.easypoi.excel.annotation.Excel; /** * <p>Title:DrillingDaily </p> * <p>Description: 钻井日报</p> * <p>Company:西安石文软件有限公司 </p> * * @author liuguiyuan * @date 2019/9/4 14:04 */ public class DrillingDaily { /** * 钻井日报 * jh:井号;ktgs:勘探公司;jd:井队;rq:日期 * sjjs:设计井深;drjs:当日井深;jc:进尺 * ztcc:钻头尺寸;ztxh:钻头型号;cw:层位 * gcjk:工程简况;zy:钻压;zs:转速;by2:泵压 * pl:排量;md:密度;nd:粘度;ss:失水;cz:纯钻 * gj:固井;sc:生产;fz:复杂;sg:事故 * xl:修理;tg:停工;qt:其他;bz:备注 */ @Excel(name = "井号") private String jh; @Excel(name = "勘探公司") private String ktgs; @Excel(name = "井队") private String jd; @Excel(name = "日期") private String rq; @Excel(name = "设计井深") private Double sjjs; @Excel(name = "当日井深") private Double drjs; @Excel(name = "进尺") private Double jc; @Excel(name = "钻头尺寸") private Double ztcc; @Excel(name = "钻头型号") private String ztxh; @Excel(name = "层位") private String cw; @Excel(name = "工程简况") private String gcjk; @Excel(name = "钻压") private String zy; @Excel(name = "转速") private String zs; @Excel(name = "泵压") private String by2; @Excel(name = "排量") private String pl; @Excel(name = "密度") private String md; @Excel(name = "粘度") private String nd; @Excel(name = "失水") private String ss; @Excel(name = "纯钻") private Double cz; @Excel(name = "固井") private Double gj; @Excel(name = "生产") private Double sc; @Excel(name = "复杂") private Double fz; @Excel(name = "事故") private Double sg; @Excel(name = "修理") private Double xl; @Excel(name = "停工") private Double tg; @Excel(name = "其他") private String qt; @Excel(name = "备注") private String bz; public String getJh() { return jh; } public void setJh(String jh) { this.jh = jh; } public String getKtgs() { return ktgs; } public void setKtgs(String ktgs) { this.ktgs = ktgs; } public String getJd() { return jd; } public void setJd(String jd) { this.jd = jd; } public String getRq() { return rq; } public void setRq(String rq) { this.rq = rq; } public Double getSjjs() { return sjjs; } public void setSjjs(Double sjjs) { this.sjjs = sjjs; } public Double getDrjs() { return drjs; } public void setDrjs(Double drjs) { this.drjs = drjs; } public Double getJc() { return jc; } public void setJc(Double jc) { this.jc = jc; } public Double getZtcc() { return ztcc; } public void setZtcc(Double ztcc) { this.ztcc = ztcc; } public String getZtxh() { return ztxh; } public void setZtxh(String ztxh) { this.ztxh = ztxh; } public String getCw() { return cw; } public void setCw(String cw) { this.cw = cw; } public String getGcjk() { return gcjk; } public void setGcjk(String gcjk) { this.gcjk = gcjk; } public String getZy() { return zy; } public void setZy(String zy) { this.zy = zy; } public String getZs() { return zs; } public void setZs(String zs) { this.zs = zs; } public String getBy2() { return by2; } public void setBy2(String by2) { this.by2 = by2; } public String getPl() { return pl; } public void setPl(String pl) { this.pl = pl; } public String getMd() { return md; } public void setMd(String md) { this.md = md; } public String getNd() { return nd; } public void setNd(String nd) { this.nd = nd; } public String getSs() { return ss; } public void setSs(String ss) { this.ss = ss; } public Double getCz() { return cz; } public void setCz(Double cz) { this.cz = cz; } public Double getGj() { return gj; } public void setGj(Double gj) { this.gj = gj; } public Double getSc() { return sc; } public void setSc(Double sc) { this.sc = sc; } public Double getFz() { return fz; } public void setFz(Double fz) { this.fz = fz; } public Double getSg() { return sg; } public void setSg(Double sg) { this.sg = sg; } public Double getXl() { return xl; } public void setXl(Double xl) { this.xl = xl; } public Double getTg() { return tg; } public void setTg(Double tg) { this.tg = tg; } public String getQt() { return qt; } public void setQt(String qt) { this.qt = qt; } public String getBz() { return bz; } public void setBz(String bz) { this.bz = bz; } }
编写修改@excel注解值的工具类
ModifyAnnotationValues
1 package com.shiwen.jdzx.server.util; 2 3 import cn.afterturn.easypoi.excel.annotation.Excel; 4 import cn.afterturn.easypoi.excel.annotation.ExcelCollection; 5 import cn.afterturn.easypoi.excel.annotation.ExcelEntity; 6 import lombok.extern.slf4j.Slf4j; 7 8 import java.lang.reflect.*; 9 import java.util.List; 10 import java.util.Map; 11 12 /** 13 * @company: 石文软件有限公司 14 * @description 15 * @author: wangjie 16 * @create: 2020-01-10 11:17 17 **/ 18 @Slf4j 19 public class ModifyAnnotationValues { 20 21 /** 22 * * 修改fields上@Excel注解的name属性,不需要下载的列,name修改增加_ignore. 23 * * 保存原来的@Excel注解name属性值,本次生成后用来恢复 24 * * @Params 25 * * headers:用户勾选,由前端传来的列名,列名的key必须和Model字段对应 26 * * clazz:model实体类 27 * * excelMap:用来记录原值的map,因为用到了递归,这里返回值作为参数传入 28 * * @return Map<String, String> 原实体类字段名和@Excel注解name属性值的映射关系<字段名,@Excel注解name属性值> 29 * 30 */ 31 public static Map<String, String> dynamicChangeAndSaveSourceAnnotation(List<String> headers, Class clazz, Map<String, String> excelMap) { 32 Field[] fields = clazz.getDeclaredFields(); 33 for (Field field : fields) { 34 // @Excel注解 35 if (field.isAnnotationPresent(Excel.class)) { 36 boolean flag = true; 37 for (int i = 0; i < headers.size(); i++) { 38 String header = headers.get(i); 39 if (field.getName().equals(header)) { 40 flag = false; 41 break; 42 } 43 } 44 // 下载列不包括该字段,进行隐藏,并记录原始值 45 if (flag) { 46 Excel annotation = field.getAnnotation(Excel.class); 47 // 保存注解 48 excelMap.put(field.getName(), annotation.name()); 49 InvocationHandler handler = Proxy.getInvocationHandler(annotation); 50 String value = annotation.name().toString(); 51 changeAnnotationValue(handler, " "); 52 } 53 // @ExcelCollection注解 54 } else if (field.isAnnotationPresent(ExcelCollection.class) && field.getType().isAssignableFrom(List.class)) { 55 Type type = field.getGenericType(); 56 if (type instanceof ParameterizedType) { 57 ParameterizedType pt = (ParameterizedType) type; 58 Class collectionClazz = (Class) pt.getActualTypeArguments()[0]; 59 // 解决@ExcelCollection如果没有需要下载列的异常,java.lang.IllegalArgumentException: The 'to' col (15) must not be less than the 'from' col (16) 60 // 如果没有需要下载列,将@ExcelCollection忽略 61 Field[] collectionFields = collectionClazz.getDeclaredFields(); 62 boolean flag = false; 63 out: 64 for (Field temp : collectionFields) { 65 if (!temp.isAnnotationPresent(Excel.class)) { 66 continue; 67 } 68 for (int i = 0; i < headers.size(); i++) { 69 String header = headers.get(i); 70 if (temp.getName().equals(header)) { 71 flag = true; 72 break out; 73 } 74 } 75 } 76 if (flag) { 77 dynamicChangeAndSaveSourceAnnotation(headers, collectionClazz, excelMap); 78 } else { 79 ExcelCollection annotation = field.getAnnotation(ExcelCollection.class); 80 excelMap.put(field.getName(), annotation.name()); 81 InvocationHandler handler = Proxy.getInvocationHandler(annotation); 82 changeAnnotationValue(handler, " "); 83 } 84 } 85 // @ExcelEntity注解 86 } else if (field.isAnnotationPresent(ExcelEntity.class)) { 87 Class entityClazz = field.getType(); 88 dynamicChangeAndSaveSourceAnnotation(headers, entityClazz, excelMap); 89 } 90 } 91 return excelMap; 92 } 93 94 // 改变注解属性值,抽取的公共方法 95 96 private static void changeAnnotationValue(InvocationHandler handler, String propertyValue) { 97 try { 98 Field field = handler.getClass().getDeclaredField("memberValues"); 99 field.setAccessible(true); 100 Map<String, Object> memberValues = (Map<String, Object>) field.get(handler); 101 memberValues.put("name", propertyValue); 102 } catch (Exception e) { 103 log.error("替换注解属性值出错!", e); 104 } 105 } 106 107 108 /** 109 * * 递归恢复@Excel原始的name属性 110 * 111 */ 112 public static void dynamicResetAnnotation(Class clazz, Map<String, String> excelMap) { 113 if (excelMap.isEmpty()) { 114 return; 115 } 116 Field[] fields = clazz.getDeclaredFields(); 117 try { 118 for (Field field : fields) { 119 if (field.isAnnotationPresent(Excel.class)) { 120 if (excelMap.containsKey(field.getName())) { 121 Excel annotation = field.getAnnotation(Excel.class); 122 InvocationHandler handler = Proxy.getInvocationHandler(annotation); 123 String sourceName = excelMap.get(field.getName()); 124 changeAnnotationValue(handler, sourceName); 125 } 126 } else if (field.isAnnotationPresent(ExcelCollection.class) && field.getType().isAssignableFrom(List.class)) { 127 // ExcelCollection修改过,才进行复原 128 if (excelMap.containsKey(field.getName())) { 129 ExcelCollection annotation = field.getAnnotation(ExcelCollection.class); 130 InvocationHandler handler = Proxy.getInvocationHandler(annotation); 131 String sourceName = excelMap.get(field.getName()); 132 changeAnnotationValue(handler, sourceName); 133 // ExcelCollection未修改过,递归复原泛型字段 134 } else { 135 Type type = field.getGenericType(); 136 if (type instanceof ParameterizedType) { 137 ParameterizedType pt = (ParameterizedType) type; 138 Class collectionClazz = (Class) pt.getActualTypeArguments()[0]; 139 dynamicResetAnnotation(collectionClazz, excelMap); 140 } 141 } 142 } else if (field.isAnnotationPresent(ExcelEntity.class)) { 143 Class entityClazz = field.getType(); 144 dynamicResetAnnotation(entityClazz, excelMap); 145 } 146 } 147 } catch (Exception e) { 148 log.error("解析动态表头,恢复注解属性值出错!", e); 149 } 150 } 151 152 153 }
编写easypoi导出的工具类
EasyPoiExcelUtil
package com.shiwen.jdzx.server.util; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.ImportParams; import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.URLEncoder; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; /** * @author wangjie * @date 2019/12/23 18:24 * @description * @company 石文软件有限公司 */ public class EasyPoiExcelUtil { public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) { ExportParams exportParams = new ExportParams(title, sheetName); exportParams.setCreateHeadRows(isCreateHeader); defaultExport(list, pojoClass, fileName, response, exportParams); } public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) { defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName)); } public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) { defaultExport(list, fileName, response); } private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) { Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list); if (workbook != null) ; downLoadExcel(fileName, response, workbook); } private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) { try { response.setHeader("content-Type", "application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); response.setCharacterEncoding("UTF-8"); workbook.write(response.getOutputStream()); } catch (IOException e) { //throw new NormalException(e.getMessage()); } } private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) { Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF); if (workbook != null) ; downLoadExcel(fileName, response, workbook); } public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) { if (StringUtils.isBlank(filePath)) { return null; } ImportParams params = new ImportParams(); params.setTitleRows(titleRows); params.setHeadRows(headerRows); List<T> list = null; try { list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params); } catch (NoSuchElementException e) { //throw new NormalException("模板不能为空"); } catch (Exception e) { e.printStackTrace(); //throw new NormalException(e.getMessage()); } return list; } public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) { if (file == null) { return null; } ImportParams params = new ImportParams(); params.setTitleRows(titleRows); params.setHeadRows(headerRows); List<T> list = null; try { list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params); } catch (NoSuchElementException e) { // throw new NormalException("excel文件不能为空"); } catch (Exception e) { //throw new NormalException(e.getMessage()); System.out.println(e.getMessage()); } return list; } }
编写控制层
DataReportExcelController
List<String> exportField参数是表头信息jh,jb......
package com.shiwen.jdzx.server.controller; import com.shiwen.jdzx.common.WellCondition; import com.shiwen.jdzx.model.daily.DrillingDaily; import com.shiwen.jdzx.server.dao.mapper.DrillingDailyDao; import com.shiwen.jdzx.server.service.DataReportExcelService; import com.shiwen.jdzx.server.util.ExportExcelUtil; import com.shiwen.jdzx.server.util.ModifyAnnotationValues; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; /** * @author wangjie * @date 2019/12/21 11:39 * @description 资料处理导出excel * @company 石文软件有限公司 */ @RestController @RequestMapping("/report") public class DataReportExcelController { @Autowired private DataReportExcelService dataReportExcelService; /** * 根据传入的表头信息灵活导出 * * @param response */ @RequestMapping("/excel/{type}") public void excel(HttpServletResponse response,@PathVariable("type") String type, String startDate, String endDate, String completed, String oilField, String firstName, String wellType,String jh,@RequestParam("exportField") List<String> exportField) throws Exception { WellCondition condition = new WellCondition(); condition.setStartDate(startDate); condition.setEndDate(endDate); condition.setOilField(oilField); condition.setFirstName(firstName); condition.setWellType(wellType); condition.setJh(jh); condition.setCompleted(completed); condition.setExport(exportField); dataReportExcelService.excel(response,condition,type); } }
编写导出的server层
WellCondition condition 封装的是条件参数
String type 导出可能是有多次导出,这个是传入的路径 比如首页需要导出 /index 就接受index就行
package com.shiwen.jdzx.server.service; import com.shiwen.jdzx.common.WellCondition; import org.springframework.ui.ModelMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author wangjie * @date 2019/12/21 11:45 * @description * @company 石文软件有限公司 */ public interface DataReportExcelService { void excel(HttpServletResponse response,WellCondition condition,String type) throws Exception; }
编写导出的server层 的实现类
package com.shiwen.jdzx.server.service.impl; import com.shiwen.jdzx.common.WellCondition; import com.shiwen.jdzx.model.OverviewDaily; import com.shiwen.jdzx.model.daily.DrillingDaily; import com.shiwen.jdzx.server.dao.mapper.DrillingDailyDao; import com.shiwen.jdzx.server.dao.mapper.WellDao; import com.shiwen.jdzx.server.service.DataReportExcelService; import com.shiwen.jdzx.server.util.Constant; import com.shiwen.jdzx.server.util.EasyPoiExcelUtil; import com.shiwen.jdzx.server.util.ModifyAnnotationValues; import com.shiwen.publics.pager.PagerOracleImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author wangjie * @date 2019/12/21 11:50 * @description * @company 石文软件有限公司 */ @Slf4j @Service public class DataReportExcelServiceImpl implements DataReportExcelService { @Autowired private DrillingDailyDao dailyDao; @Autowired private WellDao wellDao; @Override public void excel(HttpServletResponse response, WellCondition condition,String type) throws Exception { Map<String, String> excelMap=new HashMap<>(); Map<String, String> stringStringMap =null; PagerOracleImpl pager=new PagerOracleImpl(); pager.setCurPage(0); switch (type){ case Constant.DRILLING_DAILY: //修改注解 @Excel中的name值, stringStringMap=ModifyAnnotationValues.dynamicChangeAndSaveSourceAnnotation(condition.getExport(), DrillingDaily.class, excelMap); List<DrillingDaily> allDrillingDaily = dailyDao.getDrillingDaily(condition,pager); //导出excel EasyPoiExcelUtil.exportExcel(allDrillingDaily, Constant.mapList.get(type), Constant.mapList.get(type), DrillingDaily.class, Constant.mapList.get(type)+Constant.suffix, response); //导出完成恢复注解的原始值 ModifyAnnotationValues.dynamicResetAnnotation( DrillingDaily.class,stringStringMap); break; case Constant.OVERVIEW_DAILY: stringStringMap=ModifyAnnotationValues.dynamicChangeAndSaveSourceAnnotation(condition.getExport(), OverviewDaily.class, excelMap); List<OverviewDaily> overviewDailies = wellDao.listOverviewDailyAll(condition); EasyPoiExcelUtil.exportExcel(overviewDailies, Constant.mapList.get(type), Constant.mapList.get(type), OverviewDaily.class, Constant.mapList.get(type)+Constant.suffix, response); ModifyAnnotationValues.dynamicResetAnnotation(OverviewDaily.class,stringStringMap); break; case Constant.SINGLE_DRILLING_DAILY: stringStringMap=ModifyAnnotationValues.dynamicChangeAndSaveSourceAnnotation(condition.getExport(), DrillingDaily.class, excelMap); List<DrillingDaily> singleDrillingDaily = dailyDao.getDrillingDailyAll(condition,pager); EasyPoiExcelUtil.exportExcel(singleDrillingDaily, Constant.mapList.get(type), Constant.mapList.get(type), DrillingDaily.class, Constant.mapList.get(type)+Constant.suffix, response); ModifyAnnotationValues.dynamicResetAnnotation(DrillingDaily.class,stringStringMap); default: break; } } }
前段请求
1 /** 2 * 导出 3 */ 4 $scope.exportTable = function () { 5 var param = packParam(); 6 $scope.selTitleField = []; 7 $scope.selTitleName = []; 8 angular.forEach($scope.gridOption, function (item, index) { 9 if (item.checked) { 10 $scope.selTitleField.push(item.field); 11 } 12 }); 13 param.exportField = $scope.selTitleField; 14 param.jh = $scope.filter.jh; 15 var paramArr = []; 16 for (let key in param) { 17 if (param[key] || param[key] === 0) { 18 paramArr.push(key + '=' + param[key]) 19 } 20 } 21 $window.open(url.excel + '?' + paramArr.join('&')); 22 }
dao层我就不写了。以上代码根据自己的需要再改下就可以完成,根据传入的表头信息灵活导出excel,注意前段传入的表头信息,必须和实体的属性一直