Spring Boot 集成 easypoi实现excel的导入导出、excel导入导出含图片
一、Easypoi概述
1.Easypoi
easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员,就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出 ,通过简单的注解和模板语言 (熟悉的表达式语法),完成以前复杂的写法。
2.为什么要使用easypoi?
Easypoi主打的功能就是容易且易上手 。它可以让一个从没见接触过poi的开发人员,就可以很轻松写出Excel导入导出 ,而不再是看很多api才可以完成这样的工作。
它通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法,EasyPoi快速实现excel导入导出功能,且修改导出格式简单粗暴,快速有效。
3.easypoi提供了那些功能?
基于注解的导入导出,修改注解就可以修改Excel。
支持常用的样式自定义。
基于map可以灵活定义的表头字段。
支持一对多的导出,导入。
支持模板的导出,一些常见的标签,自定义标签。
支持HTML/Excel转换,如果模板还不能满足用户的变态需求,请用这个功能
支持word的导出,支持图片,Excel
4.什么场景该用哪个方法
1.ExcelExportUtil Excel导出(普通导出,模板导出 ) 2.ExcelImportUtil Excel导入 3.WordExportUtil Word导出 (只支持docx ,doc版本poi存在图片的bug,暂不支持)
4.1 导出
ExcelExportUtil.exportExcel (常用)、ExcelExportUtil.exportBigExcel 、ExcelExportUtil.exportExcelClone 、 ExcelXorHtmlUtil.htmlToExcel 、CsvExportUtil.exportCsv 、WordExportUtil.exportWord07 (常用) (1)正规excel导出 (格式简单,数据量可以,5W以内吧)
注解方式:
ExcelExportUtil.exportExcel (ExportParams entity, Class<?> pojoClass ,Collection<?> dataSet)
(2)不定多少列,但是格式依然简单数据库不大
自定义方式:
ExcelExportUtil.exportExcel (ExportParams entity, List<ExcelExportEntity> entityList ,Collection<?> dataSet)
(3)数据量大超过5W,还在100W以内
注解方式:
ExcelExportUtil.exportBigExcel (ExportParams entity, Class<?> pojoClass,IExcelExportServer server, Object queryParams)
自定义方式:
ExcelExportUtil.exportBigExcel (ExportParams entity, List excelParams,IExcelExportServer server, Object queryParams)
(4)样式复杂,数据量尽量别大
模板导出:
ExcelExportUtil.exportExcel (TemplateExportParams params , Map<String, Object> map)
(5)一次导出多个风格不一致的sheet
模板导出:
ExcelExportUtil.exportExcel (Map<Integer, Map<String, Object>> map,TemplateExportParams params)
(6)一个模板但是要导出非常多份
模板导出:
ExcelExportUtil.exportExcelClone (Map<Integer, List<Map<String, Object>>> map,TemplateExportParams params)
(7)模板无法满足你的自定义,试试html
自己构造html,然后转成excel:
ExcelXorHtmlUtil.htmlToExcel (String html, ExcelType type)
(8)数据量过百万级了.放弃excel吧,csv导出
注解方式:
CsvExportUtil.exportCsv (CsvExportParams params, Class<?> pojoClass, OutputStream outputStream)
自定义方式:
CsvExportUtil.exportCsv (CsvExportParams params, List entityList, OutputStream outputStream)
(9)word导出
模板导出:
WordExportUtil.exportWord07 (String url, Map<String, Object> map)
(9)PDF导出
模板导出: TODO
4.2 导入
ExcelImportUtil.importExcel (常用)、ExcelImportUtil.importExcelMore (常用)、ExcelImportUtil.importExcelBySax 、 CsvImportUtil.importCsv
如果想提高性能 ImportParams 的concurrentTask 可以帮助并发导入 ,仅单行,最小1000。excel有单个的那种特殊读取,readSingleCell 参数可以支持
(1)不需要检验,数据量不大(5W以内)
注解或者MAP:
ExcelImportUtil.importExcel (File file, Class<?> pojoClass, ImportParams params)
(2)需要导入,数据量不大
注解或者MAP:
ExcelImportUtil.importExcelMore (InputStream inputstream, Class<?> pojoClass, ImportParams params)
(3)数据量大了,或者你有特别多的导入操作,内存比较少,仅支持单行
SAX方式:
ExcelImportUtil.importExcelBySax (InputStream inputstream, Class<?> pojoClass, ImportParams params, IReadHandler handler)
(4)数据量超过EXCEL限制,CSV读取
小数据量:
CsvImportUtil.importCsv (InputStream inputstream, Class<?> pojoClass,CsvImportParams params)
大数据量:
CsvImportUtil.importCsv (InputStream inputstream, Class<?> pojoClass,CsvImportParams params, IReadHandler readHandler)
5.Easypoi常用注解
5.1 @Excel 注解
@Excel注解是作用到Filed上面,是对Excel一列的一个描述 ,这个注解是必须要的注解 ,其属性如下: 
5.2 @ExcelCollection 注解
@ExcelCollection 注解表示一个集合,主要针对一对多的导出 比如一个老师对应多个科目,科目就可以用集合表示,作用在一个类型是List的属性上面,属性如下:

5.3 @ExcelTarget注解
作用于最外层的对象,描述这个对象的id,以便支持一个对象可以针对不同导出做出不同处理
5.4 @ExcelEntity注解
@ExcelEntity注解表示一个继续深入导出的实体 ,是作用一个类型为实体的属性上面 
5.5 @ExcelIgnore 注解
@ExcelIgnore 和名字一样表示这个字段被忽略跳过这个导出
二、创建Excel导入导出工具类
package com.example.exceldemo.utils;
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.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
public class ExcelUtils {
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, fileName, response);
}
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
downLoadExcel(fileName, response, workbook);
}
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downLoadExcel(fileName, response, workbook);
}
public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, exportParams);
}
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
}
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
exportParams.setCreateHeadRows(isCreateHeader);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
workbook.write(response.getOutputStream());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
return importExcel(file, 1, 1, pojoClass);
}
public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
if (StringUtils.isBlank(filePath)) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
params.setNeedSave(true);
params.setSaveUrl("/excel/");
try {
return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("模板不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
if (file == null) {
return null;
}
try {
return importExcel(file.getInputStream(), titleRows, headerRows, pojoClass);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
if (inputStream == null) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
params.setSaveUrl("/excel/");
params.setNeedSave(true);
try {
return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("excel文件不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
}
三、Excel导出导入实例
1.Excel导出(一对一数据)
1.1 添加项目依赖配置
在pom.xml文件中,添加Easypoi依赖,具体依赖如下:
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.4.0</version>
</dependency>
上述依赖说明:
easypoi-base:导入导出的工具包 ,可以完成Excel导入导出,Word导出等功能。 ? easypoi-web:耦合了spring-mvc基于AbstractView,极大的简化spring-mvc下的导出功能。 ? easypoi-annotation:基础注解包,作用与实体对象上,拆分后方便maven多工程的依赖管理
但随着springboot的越来越流行,不可免俗的easypoi也为我们推出了easypoi-spring-boot-starter,方便我们使用。
所以我们就可以不再需要引入那么多相关的依赖啦,省的pom文件看着太长不好找。
如下就是easypoi的starter,大家自行复制,粘贴到本地的pom.xml中即可:
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
1.2 配置application.yml
server:
port: 8083
spring:
datasource:
name: test
url: jdbc:mysql://localhost:3306/db_user?&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSl=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 30
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
removeAbandoned: true
removeAbandonedTimeout: 180
logAbandoned: true
mvc:
static-path-pattern: /**
web:
resources:
static-locations: classpath\:/META-INF/resources/,classpath\:/resources/,classpath\:/static/,classpath\:/public/,file\:${web.upload-path}
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:/mapper/*.xml
mapper:
mappers: com.example.exceldemo.mybatis.MyMapper
identity: MYSQL
upload:
dir: F:\images\company\
1.2 定义一个导出的对象
我们先来定义一个导出vo,目的是可以对每个导出字段进行长宽等样式进行自定义化,比如我就按照数据长度判断,依次给每个字段加上width 属性,定义每个字段的展示长度。
package com.example.exceldemo.excel;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class StudentExcel implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
@Excel(name = "学生姓名", width = 30)
private String name;
@Excel(name = "学生性别", replace = {"男_1","女_0"},suffix = "生")
private Integer sex;
@Excel(name = "出生日期", databaseFormat = "yyyyMMddHHmmss",format = "yyyy-MM-dd",width = 20)
private Date birthday;
@Excel(name = "出生日期", format = "yyyy-MM-dd",width = 20)
private Date registrationDate;
private Date createTime;
private String createUser;
private Date updateTime;
private String updateUser;
}
说明:
@Excel 作用在一个filed上面,对列的描述。name:列名。orderNum:下标,从0开始。 ? 没有被注解@Excel标注的字段,不会导出。 ? 学生姓名定义了我们的列的行高 ? 学生性别因为我们基本上都是存在数据库都是数字所以我们转换下。 ? 两个日期进行了格式化输出 ? @Excel(databaseFormat="xxxx"):如果数据库字段是string类型,则需要添加该属性,并加上该时间格式,比如:databaseFormat = "yyyyMMdd"。 ? @Excel(replace={“xxx_0”, “xxx_1”,“xxx_2”},addressList = true ):表示单元格下拉框展示,_0、_1表示下拉值的前后顺序,从0往后排。要实现字段下拉,addressList属性必不可少 。 ? @Excel(suffix="xxx"):表示自动添加该xxx为你字段文字的后缀 ? @Excel(imageType=“xxx”):表示导出类型,imageType=1:从file读取;imageType=2:从数据库中读取;默认是文件,同样导入也是一样的。
1.3 在控制器中添加一个导出类
StudentController:
@RequestMapping("/exportExcel")
public void exportExcel(@RequestParam(name = "name",required = false) String name,HttpServletResponse response){
try {
StudentRequestJson requestJson = new StudentRequestJson();
if(StringUtils.isNotEmpty(name)){
requestJson.setName(name);
}
List<Student> list =studentService.getStudentList(requestJson);
String dateString = String.valueOf(System.currentTimeMillis());
String fileName = "学生信息表" + dateString;
List<StudentExcel> studentExcelList = new ListUtils<StudentExcel>().copyProperties(list,StudentExcel.class);
ExcelUtils.exportExcel(studentExcelList,StudentExcel.class,fileName,new ExportParams("计算机一班学生","学生"),response);
} catch (IOException e) {
}
}
1.4 实现Student类转成StudentExcel类
ListUtils类:Copy list
package com.example.exceldemo.utils;
import org.apache.commons.collections.CollectionUtils;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
public class ListUtils<K> {
public List<K> copyProperties(List<?> orig, Class<K> desClass) {
if (Objects.isNull(orig)) {
return null;
}
List<K> dest = new ArrayList<K>();
K des;
for (Object o : orig) {
try {
des = desClass.newInstance();
des = BeanUtils.copyAllPropertysNotNull(des, o);
dest.add(des);
} catch (Exception e) {
e.printStackTrace();
}
}
return dest;
}
public static List<Object> getFieldValueList(Collection collection, Class clazz, String fieldName) {
List<Object> objList = new ArrayList<Object>();
String methodName = "get" + getFirstUpper(fieldName);
try {
Method m = clazz.getMethod(methodName);
if (CollectionUtils.isNotEmpty(collection)) {
for (Object object : collection) {
objList.add(m.invoke(object));
}
}
} catch (Exception e) {
}
return objList;
}
public static <T> List<T> castList(Object obj, Class<T> clazz)
{
List<T> result = new ArrayList<T>();
if(obj instanceof List<?>)
{
for (Object o : (List<?>) obj)
{
result.add(clazz.cast(o));
}
return result;
}
return null;
}
public static String getFirstUpper(String str) {
String newStr = "";
if (str.length() > 0) {
newStr = str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
}
return newStr;
}
}
BeanUtils类:反射的Utils函数集合
package com.example.exceldemo.utils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiFunction;
@SuppressWarnings({"unchecked", "rawtypes", "unused"})
public class BeanUtils<T> {
private BeanUtils() {
}
public static Field getDeclaredField(Object object, String propertyName) throws NoSuchFieldException {
return getDeclaredField(object.getClass(), propertyName);
}
public static Field getDeclaredField(Class clazz, String propertyName) {
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
Field[] s = superClass.getDeclaredFields();
return superClass.getDeclaredField(propertyName);
} catch (NoSuchFieldException e) {
}
}
return null;
}
public static Object getNameProperty(Object object, String propertyName) {
Field field = null;
try {
field = getDeclaredField(object, propertyName);
} catch (NoSuchFieldException e1) {
}
Object result = null;
if (null != field) {
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
result = field.get(object);
} catch (Exception e) {
}
field.setAccessible(accessible);
}
return result;
}
public static final String FORMAT_OBJECT = "F-JSONTO-OBJ";
public static final String FORMAT_MAP = "F-JSONTO-MAP";
public static final String FORMAT_LIST = "F-JSONTO-LIST";
public static final String FORMAT_GET = "F-GET";
public static final String TO_JSON = "TO-JSON";
public static Object forceGetProperty(Object object, String propertyName) {
Object result = null;
try {
if (object instanceof Map) {
result = ((Map) object).get(propertyName);
} else {
result = getObjValue(object, propertyName, null);
}
} catch (Exception e) {
}
return result;
}
public static Map<String, Object> toReplaceKeyLow(Map<String, Object> map) {
Map re_map = new HashMap();
if (re_map != null) {
Iterator var2 = map.entrySet().iterator();
while (var2.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry) var2.next();
re_map.put(underlineToCamel((String) entry.getKey()), map.get(entry.getKey()));
}
map.clear();
}
return re_map;
}
public static String underlineToCamel(String origin) {
return stringProcess(
origin, (prev, c) -> {
if (prev == '_' && Character.isLowerCase(c)) {
return "" + Character.toUpperCase(c);
}
if (c == '_') {
return "";
}
return "" + c;
}
);
}
public static String stringProcess(String origin, BiFunction<Character, Character, String> convertFunc) {
if (origin == null || "".equals(origin.trim())) {
return "";
}
String newOrigin = "0" + origin;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < newOrigin.length() - 1; i++) {
char prev = newOrigin.charAt(i);
char c = newOrigin.charAt(i + 1);
sb.append(convertFunc.apply(prev, c));
}
return sb.toString();
}
public static void setNameProperty(Object object, String propertyName, Object newValue) throws NoSuchFieldException {
Field field = getDeclaredField(object, propertyName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
field.set(object, newValue);
} catch (Exception e) {
}
field.setAccessible(accessible);
}
public static void forceSetProperty(Object object, String propertyName, Object newValue) {
if (null == object || StringUtils.isEmpty(propertyName)) {
return;
}
String[] s = propertyName.split("\\.");
if (null == s) {
return;
}
for (int i = 0; i < s.length - 1; i++) {
object = forceGetProperty(object, s[i]);
}
try {
if (object instanceof Map) {
((Map) object).put(propertyName, newValue);
} else {
setObjValue(object, underlineToCamel(propertyName), newValue);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object invokePrivateMethod(Object object, String methodName, Object... params)
throws NoSuchMethodException {
Class[] types = new Class[params.length];
for (int i = 0; i < params.length; i++) {
types[i] = params[i].getClass();
}
Class clazz = object.getClass();
Method method = null;
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
method = superClass.getDeclaredMethod(methodName, types);
break;
} catch (NoSuchMethodException e) {
}
}
if (method == null) {
throw new NoSuchMethodException("No Such Method:" + clazz.getSimpleName() + methodName);
}
boolean accessible = method.isAccessible();
method.setAccessible(true);
Object result = null;
try {
result = method.invoke(object, params);
} catch (Exception e) {
}
method.setAccessible(accessible);
return result;
}
public static Method transferMethoder(String classpath, String methodname, Class types[]) {
Class clazz = null;
try {
clazz = Class.forName(classpath);
} catch (Exception e) {
}
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getMethod(methodname, types);
} catch (Exception e) {
}
}
return null;
}
public static Method transferMethoder(Object obj, String methodname, Class types[]) {
Class clazz = obj.getClass();
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getMethod(methodname, types);
} catch (Exception e) {
}
}
return null;
}
public static Field[] getObjProperty(Object obj) {
Class c = obj.getClass();
Field[] field = c.getDeclaredFields();
return field;
}
public static void copySupperPropertys(Object arg0, Object arg1) throws Exception {
if (null != arg0 && null != arg1) {
Object value = null;
if (arg1 instanceof Map) {
for (String key : ((Map<String, Object>) arg1).keySet()) {
value = BeanUtils.forceGetProperty(arg1, key);
BeanUtils.forceSetProperty(arg0, key, value);
}
} else {
Field[] field = getObjSupperProperty(arg1);
if (null != field) {
for (int i = 0; i < field.length; i++) {
value = BeanUtils.forceGetProperty(arg1, field[i].getName());
BeanUtils.forceSetProperty(arg0, field[i].getName(), value);
}
}
}
} else {
throw new Exception("参数为空");
}
}
public static void copyAllPropertys(Object arg0, Object arg1) throws Exception {
if (null != arg0 && null != arg1) {
Object value = null;
if (arg1 instanceof Map) {
for (String key : ((Map<String, Object>) arg1).keySet()) {
value = BeanUtils.forceGetProperty(arg1, key);
BeanUtils.forceSetProperty(arg0, key, value);
}
} else {
Field[] field = getObjAllProperty(arg1);
if (null != field) {
for (int i = 0; i < field.length; i++) {
if ("serialVersionUID".equals(field[i].getName())) {
continue;
}
value = BeanUtils.forceGetProperty(arg1, field[i].getName());
BeanUtils.forceSetProperty(arg0, field[i].getName(), value);
}
}
}
} else {
throw new Exception("参数为空");
}
}
public static void copyImplPropertys(Object arg0, Object arg1) throws Exception {
if (null != arg0 && null != arg1) {
Object value = null;
if (arg1 instanceof Map) {
for (String key : ((Map<String, Object>) arg1).keySet()) {
value = BeanUtils.forceGetProperty(arg1, key);
BeanUtils.forceSetProperty(arg0, key, value);
}
} else {
Field[] field = getObjProperty(arg1);
if (null != field) {
for (int i = 0; i < field.length; i++) {
value = BeanUtils.forceGetProperty(arg1, field[i].getName());
BeanUtils.forceSetProperty(arg0, field[i].getName(), value);
}
}
}
} else {
throw new Exception("参数为空");
}
}
public static String checkImplNull(Object arg0, Map<String, String> notcl) {
if (null == arg0) {
return "null";
}
Object value = null;
String msg = "";
if (arg0 instanceof Map) {
for (String key : ((Map<String, Object>) arg0).keySet()) {
if ("serialVersionUID".equals(key) || notcl.containsKey(key)) {
continue;
}
value = BeanUtils.forceGetProperty(arg0, key);
if (null == value) {
msg += key + " is null ";
} else if (value instanceof String) {
if (StringUtils.isEmpty(value.toString())) {
msg += key + " is null ";
}
}
}
} else {
Field[] field = getObjProperty(arg0);
if (null != field) {
for (int i = 0; i < field.length; i++) {
if ("serialVersionUID".equals(field[i].getName()) || notcl.containsKey(field[i].getName())) {
continue;
}
value = BeanUtils.forceGetProperty(arg0, field[i].getName());
if (null == value) {
msg += field[i].getName() + " is null ";
} else if (value instanceof String) {
if (StringUtils.isEmpty(value.toString())) {
msg += field[i].getName() + " is null ";
}
}
}
}
}
return msg;
}
public static Field[] getObjSupperProperty(Object obj) {
Class c = obj.getClass();
Class supper = c.getSuperclass();
List<Field> list = new ArrayList<Field>();
if (null != supper) {
for (Class superClass = supper; superClass != Object.class; superClass = superClass.getSuperclass()) {
Field[] fieldchild = superClass.getDeclaredFields();
if (null != fieldchild) {
for (Field field2 : fieldchild) {
list.add(field2);
}
}
}
}
Field[] field = new Field[list.size()];
field = list.toArray(field);
return field;
}
public static Field[] getObjOpSupperProperty(Object obj) {
Class c = obj.getClass();
Class supper = c.getSuperclass();
List<Field> list = new ArrayList<Field>();
if (null != supper) {
for (Class superClass = supper; superClass != Object.class; superClass = superClass.getSuperclass()) {
Field[] fieldchild = superClass.getDeclaredFields();
if (null != fieldchild) {
for (Field field2 : fieldchild) {
list.add(field2);
}
}
}
}
Field[] field = new Field[list.size()];
field = list.toArray(field);
return field;
}
public static Field[] getObjAllProperty(Object obj) {
List<Field> list = new ArrayList<Field>();
for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
Field[] fieldchild = superClass.getDeclaredFields();
if (null != fieldchild) {
for (Field field2 : fieldchild) {
list.add(field2);
}
}
}
Field[] field = new Field[list.size()];
field = list.toArray(field);
return field;
}
public static Field[] getObjAllOpProperty(Object obj) {
List<Field> list = new ArrayList<Field>();
for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
Field[] fieldchild = superClass.getDeclaredFields();
if (null != fieldchild) {
for (Field field2 : fieldchild) {
list.add(field2);
}
}
}
Field[] field = new Field[list.size()];
field = list.toArray(field);
return field;
}
public static String getProNameMethod(String proName) {
String methodName = "";
if (StringUtils.isNotEmpty(proName)) {
methodName = "get" + getFirstUpper(proName);
}
return methodName;
}
public static String getProSetNameMethod(String proName) {
String methodName = "";
if (StringUtils.isNotEmpty(proName)) {
methodName = "set" + getFirstUpper(proName);
}
return methodName;
}
public static Object getObjValue(Object obj, String name, Object defObj) {
Object valueObj = null;
String methodName = getProNameMethod(name);
Method method = transferMethoder(obj, methodName, new Class[0]);
if (null != method) {
try {
valueObj = method.invoke(obj);
if (null == valueObj) {
valueObj = defObj;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return valueObj;
}
public static void setObjValue(Object obj, String name, Object defObj) {
String methodName = getProSetNameMethod(name);
try {
Field field = getDeclaredField(obj, name);
if (null == field) {
return;
}
Class fclass = field.getType();
Object valueobj = getValueByType(fclass, defObj);
Class[] types = {fclass};
Method method = transferMethoder(obj, methodName, types);
if (null != method) {
method.invoke(obj, valueobj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getValueByType(Class cls, Object defObj) {
Object obj = defObj;
if (cls == null) {
return obj;
}
String className = cls.getName();
if (null == className) {
return obj;
}
if (className.indexOf("[") == 0) {
return obj;
} else if (null != defObj && defObj.getClass().getName().equals(className)) {
return obj;
} else {
if (className.indexOf("String") >= 0) {
if (null == defObj) {
obj = null;
} else {
obj = defObj + "";
}
} else if (className.indexOf("int") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Long.valueOf(String.valueOf(defObj)).intValue();
} else if (className.indexOf("Long") >= 0 || className.indexOf("long") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Long.valueOf(String.valueOf(defObj));
} else if (className.indexOf("Number") >= 0 || className.indexOf("number") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Long.valueOf(String.valueOf(defObj));
} else if (className.indexOf("Double") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Double.valueOf(String.valueOf(defObj));
} else if (className.indexOf("double") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Double.valueOf(String.valueOf(defObj));
} else if (className.indexOf("Date") >= 0) {
if (null != defObj && StringUtils.isNotEmpty(String.valueOf(defObj))) {
if (String.valueOf(defObj).length() > 10) {
obj = DateUtils.getDateToString(String.valueOf(defObj), DateUtils.DATETIMESHOWFORMAT);
} else {
obj = DateUtils.getDateToString(String.valueOf(defObj), DateUtils.DATESHOWFORMAT);
}
if (obj == null) {
obj = defObj;
}
}
} else if (className.indexOf("Integer") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Integer.valueOf(String.valueOf(defObj));
} else if (className.indexOf("boolean") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "false";
}
if ("true".equals(String.valueOf(defObj))) {
obj = true;
} else {
obj = false;
}
} else if (className.indexOf("Boolean") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "false";
}
if ("true".equals(String.valueOf(defObj))) {
obj = true;
} else {
obj = false;
}
} else if (cls.isEnum()) {
obj = Enum.valueOf(cls, defObj.toString());
} else if (className.indexOf("BigDecimal") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
} else {
obj = new BigDecimal(String.valueOf(defObj));
}
} else if (className.indexOf("Short") >= 0) {
if (null == defObj || StringUtils.isEmpty(String.valueOf(defObj))) {
defObj = "0";
}
obj = Short.valueOf(String.valueOf(defObj));
}
}
return obj;
}
public static void setObjValue(Object obj, String name, String defObj) {
String methodName = getProSetNameMethod(name);
try {
Field field = getDeclaredField(obj, name);
Class fclass = field.getType();
Class[] types = {fclass};
Method method = transferMethoder(obj, methodName, types);
if (null != method) {
method.invoke(obj, getStringToType(fclass, defObj));
}
} catch (Exception e) {
}
}
public static Object getObject(Object obj, String name, String defObj) {
String methodName = getProSetNameMethod(name);
try {
Field field = getDeclaredField(obj, name);
Class fclass = field.getType();
Class[] types = {fclass};
return getStringToType(fclass, defObj);
} catch (Exception e) {
}
return null;
}
public static String getObjectHql(Object obj, String name, List<Object> paramlist, Object value) {
String methodName = getProSetNameMethod(name);
try {
Field field = getDeclaredField(obj, name);
Class fclass = field.getType();
Class[] types = {fclass};
return getStringToHql(fclass, name, paramlist, value);
} catch (Exception e) {
}
return null;
}
public static Object getStringToType(Class typeClass, String value) {
Object obj = null;
if (typeClass.equals(String.class)) {
if (null == value || StringUtils.isEmpty(value)) {
obj = "";
} else {
obj = String.valueOf(value);
}
} else if (typeClass.equals(Double.class)) {
if (null == value || StringUtils.isEmpty(value)) {
obj = 0D;
} else {
obj = Double.valueOf(value);
}
} else if (typeClass.equals(Integer.class)) {
if (null == value || StringUtils.isEmpty(value)) {
obj = 0;
} else {
obj = Integer.valueOf(value);
}
} else if (typeClass.equals(Date.class)) {
if (null == value || StringUtils.isEmpty(value)) {
obj = null;
} else {
obj = DateUtils.getDateToString(value, DateUtils.DATETIMESHOWFORMAT);
}
} else if (typeClass.equals(Long.class)) {
if (null == value || StringUtils.isEmpty(value)) {
obj = 0L;
} else {
obj = Long.valueOf(value);
}
} else if (typeClass.equals(BigDecimal.class)) {
if (null == value || StringUtils.isEmpty(value)) {
} else {
obj = new BigDecimal(value);
}
} else {
obj = 0;
}
return obj;
}
@SuppressWarnings("deprecation")
public static String getStringToHql(Class typeClass, String name, List<Object> paramlist, Object value) {
String obj = null;
if (typeClass.equals(String.class)) {
obj = "'--'";
paramlist.add(null == value || "".equals(value) ? "--" : value);
} else if (typeClass.equals(Double.class)) {
obj = "0";
paramlist.add(null == value || "".equals(value) ? 0D : value);
} else if (typeClass.equals(Integer.class)) {
obj = "0";
paramlist.add(null == value || "".equals(value) ? 0 : value);
} else if (typeClass.equals(Date.class)) {
obj = "to_date('1991.01.01','yyyy.mm.dd')";
paramlist.add(null == value || "".equals(value) ? new Date("1991.01.01") : value);
} else if (typeClass.equals(Long.class)) {
obj = "0";
paramlist.add(null == value || "".equals(value) ? 0L : value);
} else {
obj = "0";
paramlist.add(null == value || "".equals(value) ? 0 : value);
}
return obj;
}
public static <T> T copyAllPropertysNotNull(T arg0, Object arg1) throws Exception {
if (null != arg0 && null != arg1) {
Object value = null;
if (arg1 instanceof Map) {
for (String key : ((Map<String, Object>) arg1).keySet()) {
value = BeanUtils.forceGetProperty(arg1, key);
if (value == null) {
continue;
}
BeanUtils.forceSetProperty(arg0, key, value);
}
} else {
Field[] field = getObjAllProperty(arg1);
if (null != field) {
for (int i = 0; i < field.length; i++) {
value = BeanUtils.forceGetProperty(arg1, field[i].getName());
if (value == null) {
continue;
}
BeanUtils.forceSetProperty(arg0, field[i].getName(), value);
}
}
}
} else {
throw new Exception("参数为空");
}
return arg0;
}
private static List<Field> getFieldList(Class clazz, List<Field> fieldList) {
Field[] fields = clazz.getDeclaredFields();
fieldList.addAll(Arrays.asList(fields));
Field[] pFields = clazz.getSuperclass().getDeclaredFields();
if (pFields != null && pFields.length > 0) {
fieldList.addAll(Arrays.asList(pFields));
getFieldList(clazz.getSuperclass(), fieldList);
} else {
return fieldList;
}
return fieldList;
}
public static String getFirstUpper(String str) {
String newStr = "";
if (str.length() > 0) {
newStr = str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
}
return newStr;
}
}
DateUtils类:时间工具类
package com.example.exceldemo.utils;
import org.apache.commons.lang3.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class DateUtils {
public static final String DATESHOWFORMAT = "yyyy-MM-dd";
public static final String DATETIMESHOWFORMAT = "yyyy-MM-dd HH:mm:ss";
public static Date getDateToString(String dateStr, String patten) {
if (StringUtils.isEmpty(dateStr)) {
return null;
}
SimpleDateFormat formatter = new SimpleDateFormat(patten,Locale.CHINA);
try {
return formatter.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
1.5 浏览器测试
在浏览器中输入:http://localhost:8083/student/exportExcel,然后键盘回车,你将会看到,浏览器左下方,会弹出一个excel下载窗口,这就意味着已经导出excel成功了。

等待下载完成,我们打开excel文件,可以检查一下你代码中所设置的文件标题,字段名,字段长度,文件名等是否都设置成功了。
导出excel文件打开后实际效果如下:
 可以看到,上方截图已经证明导出方法完全正确,所设置的内容都设置上了,比如每个字段的长度、日期格式化、性别转换,就是按代码所设置的属性来进行展示的。
2.Excel导出(一对多数据)
2.1 定义一个导出的集合
一个课程对应一个老师、一个课程对应N个学生
(1)课程的实体
package com.example.exceldemo.excel;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelEntity;
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
@ExcelTarget("courseExcel")
public class CourseExcel implements Serializable {
private static final long serialVersionUID = 1L;
@Excel(name = "课程姓名", width = 30,needMerge = true, orderNum = "1")
private String name;
@ExcelEntity(id="absent")
private TeacherExcel teacherExcel;
@ExcelCollection(name="学生", orderNum = "3")
private List<StudentExcel> studentExcelList;
}
说明:
@ExcelTarget 这个是作用于最外层的对象,描述这个对象的id,以便支持一个对象可以针对不同导出做出不同处理 ? @ExcelEntity 表示一个继续深入导出的实体 ,但他没有太多的实际意义,只是告诉系统这个对象里面同样有导出的字段 ? @ExcelCollection 表示一个集合,主要针对一对多的导出 ,比如一个老师对应多个科目,科目就可以用集合表示 ? @Excel 中 needMerge:是否需要合并
(2)老师的实体
package com.example.exceldemo.excel;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
@Data
public class TeacherExcel implements Serializable {
private static final long serialVersionUID = 1L;
@Excel(name = "主讲老师_major,代课老师_absent",needMerge = true, width = 20,orderNum = "1")
private String name;
}
这里一对多导出的实体就创建好了。下面来实现一对多导出代码。
2.2 在控制器类中添加一个导出方法
@RequestMapping("/exportExcel")
public void exportExcel(@RequestParam(name = "name",required = false) String name,HttpServletResponse response){
try {
CourseRequestJson requestJson = new CourseRequestJson();
if(StringUtils.isNotEmpty(name)){
requestJson.setName(name);
}
List<CourseVo> list =courseService.getCourseExportList(requestJson);
String dateString = String.valueOf(System.currentTimeMillis());
String fileName = "课程学生信息表" + dateString;
List<CourseExcel> CourseExcelList = new ListUtils<CourseExcel>().copyProperties(list,CourseExcel.class);
ExcelUtils.exportExcel(CourseExcelList,CourseExcel.class,fileName,new ExportParams("计算机一班","课程学生"),response);
} catch (IOException e) {
}
}
2.3 一对多查询
List<CourseVo> list =courseService.getCourseExportList(requestJson);
CourseVo实体如下:
package com.example.exceldemo.dto;
import com.example.exceldemo.excel.StudentExcel;
import com.example.exceldemo.excel.TeacherExcel;
import lombok.Data;
import java.util.List;
@Data
public class CourseVo{
private Integer id;
private String name;
private TeacherExcel teacherExcel;
private List<StudentExcel> studentExcelList;
}
CourseMapper创建查询方法:
public interface CourseMapper extends MyMapper<Course> {
List<CourseVo> getCourseExportList(CourseRequestJson CourseRequestJson);
List<StudentExcel> getStudentListById(@Param("id") Integer courseId);
}
借助于 association标签(1对1)、collection标签(1对多)实现查询效果:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.exceldemo.dao.CourseMapper">
<resultMap id="BaseResultMap_1" type="com.example.exceldemo.dto.CourseVo">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<association property="teacherExcel" javaType="com.example.exceldemo.excel.TeacherExcel">
<result property="name" column="teacher_name"></result>
</association>
<collection property="studentExcelList" column="id" ofType="com.example.exceldemo.excel.StudentExcel" select="com.example.exceldemo.dao.CourseMapper.getStudentListById" >
</collection>
</resultMap>
<select id="getCourseExportList" resultMap="BaseResultMap_1">
select c.id,
c.`name`,
t.`name` as teacher_name
from t_course c
LEFT JOIN t_teacher t on t.id = c.teacher_id
where 1=1
<if test="name!=null and ''!=name">
and c.`name` like concat('%',#{name},'%')
</if>
</select>
<select id="getStudentListById" resultType="com.example.exceldemo.excel.StudentExcel">
select s1.id,s1.name,s1.sex,s1.birthday,s1.registration_date as registrationDate
from t_course_student c1
INNER JOIN t_student s1 on s1.id = c1.student_id
where course_id=#{id}
</select>
</mapper>
2.4 浏览器测试
在浏览器中输入:http://localhost:8083/course/exportExcel?name=,然后键盘回车,你将会看到,浏览器左下方,会弹出一个excel下载窗口,这就意味着已经导出excel成功了。  excel实现效果如下:

3.含图片的Excel导出
在日常运作中不可避免的会遇到图片的导入导出,这里提供了两种类型的图片导出方式:
3.1 方式1:从file读取(type = 2,imageType=1)
@Excel(name = "公司LOGO", type = 2 ,width = 40 , height = 20,imageType = 1)
private String companyLogo;
注意: type =2 表示该字段类型为图片 ,imageType=1 (默认可以不填),表示从file读取,字段类型是个字符串类型 。
可以用相对路径 也可以用绝对路径 ,绝对路径优先依次获取
3.2 方式2:从数据库或者已经读取完毕(type = 2,imageType=2)
@Excel(name = "公司LOGO", type = 2 ,width = 40 , height = 20,imageType = 2)
private byte[] companyLogo;
注意: type =2 表示该字段类型为图片,imageType=2 ,表示从数据库或者已经读取完毕,字段类型是个字节数组 直接使用 。同时,image 类型的cell最好设置好宽和高,会百分百缩放到cell那么大,不是原尺寸。
3.3 代码示例:以方式1为例
easypoi依赖、application配置上面的导出介绍过了,不再累赘。
3.3.1 实体类
package com.example.exceldemo.excel;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import java.io.Serializable;
@Data
public class CompanyExcel implements Serializable {
@Excel(name = "公司名称")
private String name;
@Excel(name = "公司Logo", width = 40, height = 20, type = 2, savePath = "F:\\company")
private String logo;
@Excel(name = "公司地址", width = 60)
private String address;
}
3.3.2 本地图片地址映射
package com.example.exceldemo.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${upload.dir}")
private String filePath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/company/**").addResourceLocations("file:"+filePath);
}
}
3.3.2 在控制器类中添加一个导出方法
CompanyController:
@RequestMapping("/exportExcel")
public void exportExcel(HttpServletResponse response){
try {
List<Company> companyList = companyService.findAll();
List<CompanyExcel> companyExcelList = new ListUtils<CompanyExcel>().copyProperties(companyList, CompanyExcel.class);
String dateString = String.valueOf(System.currentTimeMillis());
String fileName = "公司信息" + dateString;
ExportParams params = new ExportParams();
params.setTitle("公司信息列表");
params.setSheetName("公司信息");
params.setType(ExcelType.HSSF);
ExcelUtils.exportExcel(companyExcelList, CompanyExcel.class,fileName,params,response);
}catch (Exception e){
}
}
注意: 需要设置ExcelType.HSSF,不然图片不显示
3.3.2 浏览器测试
在浏览器中输入:http://localhost:8083/company/exportExcel,然后键盘回车,你将会看到,浏览器左下方,会弹出一个excel下载窗口,这就意味着已经导出excel成功了。 
excel实现效果如下:

4.含图片的Excel导入
easypoi依赖、application.yml配置同上。
4.1 实体类
同上面含图片的Excel导出。
4.2 本地图片地址映射
同上面含图片的Excel导出。
4.3 新建要上传的excel文件

4.4 在控制器类中添加一个导出方法
CompanyController:
@RequestMapping("/upload")
public Result importExcel(@RequestParam("file") MultipartFile file,HttpServletRequest request){
try{
String originalFileName = file.getOriginalFilename();
List<CompanyExcel> companyExcelList = ExcelUtils.importExcel(file,1,1,CompanyExcel.class);
List<Company> companyList = new ListUtils<Company>().copyProperties(companyExcelList,Company.class);
saveImage(request,companyList);
companyService.addBatchCompany(companyList);
return Result.success("上传成功:"+originalFileName);
}catch (Exception e){
e.printStackTrace();
return Result.fail("上传失败");
}
}
注意: excel文件中有标题并占用一行,所以setTitleRows设置为1。表头也占用一行,所以setHeadRows设置为1; 
application.yml 中配置图片上传路径
upload:
dir: F:\images\company\
图片转存:将图片存储到本地磁盘中
@Value("${upload.dir}")
private String filePath;
private void saveImage(HttpServletRequest request, List<Company> companyList) {
for (Company company : companyList) {
if (StringUtils.isNotEmpty(company.getLogo())) {
try {
File tmpFile = new File(company.getLogo());
FileInputStream fileInputStream = new FileInputStream(tmpFile);
MultipartFile multipartFile = new MockMultipartFile("file", tmpFile.getName(), "text/plain", IOUtils.toByteArray(fileInputStream));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
String format = sdf.format(new Date());
File file = new File(filePath + format);
if (!file.isDirectory()) {
file.mkdirs();
}
String originName = tmpFile.getName();
String suffix = originName.substring(originName.lastIndexOf("."));
String fileName = UUID.randomUUID() + suffix;
File dest = new File(file.getAbsoluteFile() + File.separator + fileName);
String filePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/company/" + format + fileName;
multipartFile.transferTo(dest);
company.setLogo(filePath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4.5 postman测试excel导入接口
第一步:先将url填写完整,然后参数传入,选择Body-->form-data ;以数据表格的方式提交 , 然后参数填写你固定的参数名,参数类型选择file 而不是默认的text文本,其value选择你刚才创建好的excel导入文件,选择添加即可。
具体演示如下:
 第二步:点击Send,请求接口。查看接口结果返回。  控制台sq执行日志如下:  excel导入成功!!!
四、完整代码
点击此处进行下载
|