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;
/**
* Excel 导入导出工具类
* @author qzz
*/
public class ExcelUtils {
/**
* excel 导出
*
* @param list 数据列表
* @param fileName 导出时的excel名称
* @param response
*/
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, fileName, response);
}
/**
* 默认的 excel 导出
*
* @param list 数据列表
* @param fileName 导出时的excel名称
* @param response
*/
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
//把数据添加到excel表格中
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
downLoadExcel(fileName, response, workbook);
}
/**
* excel 导出
*
* @param list 数据列表
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param response
* @param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)
*/
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
//把数据添加到excel表格中
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downLoadExcel(fileName, response, workbook);
}
/**
* excel 导出
*
* @param list 数据列表
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)
* @param response
*/
public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* excel 导出
*
* @param list 数据列表
* @param title 表格内数据标题
* @param sheetName sheet名称
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param response
*/
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));
}
/**
* excel 导出
*
* @param list 数据列表
* @param title 表格内数据标题
* @param sheetName sheet名称
* @param pojoClass pojo类型
* @param fileName 导出时的excel名称
* @param isCreateHeader 是否创建表头
* @param response
*/
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);
}
/**
* excel下载
*
* @param fileName 下载时的文件名称
* @param response
* @param workbook excel数据
*/
private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
try {
response.setCharacterEncoding("UTF-8");
//设置信息头,告诉浏览器内容为excel类型
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());
}
}
/**
* excel 导入
*
* @param file excel文件
* @param pojoClass pojo类型
* @param <T>
* @return
*/
public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
return importExcel(file, 1, 1, pojoClass);
}
/**
* excel 导入
*
* @param filePath excel文件路径
* @param titleRows 表格内数据标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
* @param <T>
* @return
*/
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());
}
}
/**
* excel 导入
*
* @param file 上传的文件
* @param titleRows 表格内数据标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
* @param <T>
* @return
*/
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());
}
}
/**
* excel 导入
*
* @param inputStream 文件输入流
* @param titleRows 表格内数据标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
* @param <T>
* @return
*/
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依赖,具体依赖如下:
<!--easypoi依赖,实现excel文件导入导出、word导出等-->
<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中即可:
<!--easypoi依赖,实现excel文件导入导出-->
<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,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 超过时间限制是否回收
removeAbandoned: true
# 超时时间;单位为秒。180秒=3分钟
removeAbandonedTimeout: 180
# 关闭abanded连接时输出错误日志
logAbandoned: true
#静态资源路径
mvc:
static-path-pattern: /**
web:
resources:
static-locations: classpath\:/META-INF/resources/,classpath\:/resources/,classpath\:/static/,classpath\:/public/,file\:${
web.upload-path}
##配置打印sql
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#默认扫描mapper.xml
mapper-locations: classpath:/mapper/*.xml
#mybatis&&通用mapper
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;
/**
* 学生
* @author qzz
*/
@Data
public class StudentExcel implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
/**
* @Excel 作用在一个filed上面,对列的描述
* @param name 列名
* @param orderNum 下标,从0开始。
*/
@Excel(name = "学生姓名", width = 30)
private String name;
/**
* 学生性别 1:男 2:女
*/
@Excel(name = "学生性别", replace = {
"男_1","女_0"},suffix = "生")
private Integer sex;
/**
* 出生日期
* 如果数据库是string类型,这个需要设置这个数据库时间格式(databaseFormat) format:输出时间格式
*/
@Excel(name = "出生日期", databaseFormat = "yyyyMMddHHmmss",format = "yyyy-MM-dd",width = 20)
private Date birthday;
/**
* 进校日期
* 字段是Date类型则不需要设置databaseFormat
*/
@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:
/**
* 导出
* @param name
*/
@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);
}
//1.从数据库查询到数据
List<Student> list =studentService.getStudentList(requestJson);
//2.导出时的excel名称
String dateString = String.valueOf(System.currentTimeMillis());
String fileName = "学生信息表" + dateString;
//3.查看结果集转成导出vo(转成对应的类型;要不然会报错)
List<StudentExcel> studentExcelList = new ListUtils<StudentExcel>().copyProperties(list,StudentExcel.class);
//4.工具类导出excel
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> {
/**
* Copy list
*
* @param orig
* @param desClass
* @return
*/
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;
}
/**
* 将List<**Domain>转换为List<**fidld>
*
* @param collection 数据源
* @param clazz domain名称
* @param fieldName 字段名称
* @return
*/
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;
/**
* <p>
* Note:反射的Utils函数集合.扩展自Apache Commons BeanUtil
* <p>
* Date: 06/15/2015 14:37.
*
* @author He Jialin
*/
@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;
}
/**
* 直接获取属性值
*
* @param object
* @param propertyName
* @return
*/
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();
}
/**
* 直接赋属性值
*
* @param object
* @param propertyName
* @return
*/
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);
}
/**
* 通过set方法赋值
*
* @param object
* @param propertyName
* @param newValue
*/
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;
}
/**
* 通过反射动态调用方法
*
* @param classpath 类
* @param methodname 方法名称
* @param types [] 方法参数数组
* @return
*/
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;
}
/**
* 通过反射动态调用方法
*
* @param obj 类
* @param methodname 方法名称
* @param types [] 方法参数数组
* @return
*/
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;
}
/**
* 获取对象的属性(不包括继承的)
*
* @param obj
* @return Field[]
*/
public static Field[] getObjProperty(Object obj) {
Class c = obj.getClass();
Field[] field = c.getDeclaredFields();
return field;
}
/**
* 只拷贝超类里的数据
*
* @param arg0
* @param arg1
* @throws Exception
*/
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("参数为空");
}
}
/**
* 拷贝类里所有的数据
*
* @param arg0 dest
* @param arg1
* @throws 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("参数为空");
}
}
/**
* 拷贝的数据(不包括继承的)
*
* @param arg0
* @param arg1
* @throws 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;
}
/**
* 获取对象祖先的属性
*
* @param obj
* @return Field[]
*/
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;
}
/**
* 获取对象祖先的属性,不包括supperbean
*
* @param obj
* @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;
}
/**
* 获取对象所有的属性(包括继承的)
*
* @param obj
* @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;
}
/**
* 获取对象所有的属性(包括继承的),不包括supperbean
*
* @param obj
* @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;
}
/**
* 获取对应的名称的get方法
*
* @param proName
* @return
*/
public static String getProNameMethod(String proName) {
String methodName = "";
if (StringUtils.isNotEmpty(proName)) {
methodName = "get" + getFirstUpper(proName);
}
return methodName;
}
/**
* 获取对应的名称的set方法
*
* @param proName
* @return
*/
public static String getProSetNameMethod(String proName) {
String methodName = "";
if (StringUtils.isNotEmpty(proName)) {
methodName = "set" + getFirstUpper(proName);
}
return methodName;
}
/**
* 获取对象里对应的属性值(通过get方法)
*
* @param obj
* @param name
* @param defObj 默认值
* @return
*/
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;
}
/**
* 赋值对象里对应的属性值(通过set方法)
*
* @param obj
* @param name
* @param defObj 值
* @return
*/
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();
}
}
/**
* @param cls
* @param defObj
* @return
*/
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))) {
//obj = BigDecimal.ZERO;
} 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;
}
/**
* 赋值对象里对应的属性值(通过set方法) defObj固定式string,要转换
*
* @param obj
* @param name
* @param defObj 值
* @return
*/
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;
}
/**
* 把string 转化成对应的类型
*
* @param typeClass
* @param value
* @return
*/
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)) {
//obj = BigDecimal.ZERO;
} 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;
/**
* 时间工具类
* @author qzz
*/
public class DateUtils {
/**
* Format:yyyy-MM-dd
*/
public static final String DATESHOWFORMAT = "yyyy-MM-dd";
/**
* Format:yyyy-MM-dd HH:mm:ss
*/
public static final String DATETIMESHOWFORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* 日期格式化(String转换为Date)
*
* @param dateStr
* 日期字符串
* @param patten
* 处理结果日期的显示格式,如:"YYYY-MM-DD"
* @return
*/
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;
/**
* 课程
* @author qzz
*/
@Data
@ExcelTarget("courseExcel")
public class CourseExcel implements Serializable {
private static final long serialVersionUID = 1L;
/**
* @Excel 作用在一个filed上面,对列的描述
* @param name 列名
* @param orderNum 下标,从0开始。
* @param needMerge 是否需要合并
*/
@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;
/**
* 老师
* @author qzz
*/
@Data
public class TeacherExcel implements Serializable {
private static final long serialVersionUID = 1L;
/**
* @Excel 作用在一个filed上面,对列的描述
* @param name 列名
* @param orderNum 下标,从0开始。
*/
@Excel(name = "主讲老师_major,代课老师_absent",needMerge = true, width = 20,orderNum = "1")
private String name;
}
这里一对多导出的实体就创建好了。下面来实现一对多导出代码。
2.2 在控制器类中添加一个导出方法
/**
* 导出(一对多)
* @param name
*/
@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);
}
//1.从数据库查询到数据(一对多查询)
List<CourseVo> list =courseService.getCourseExportList(requestJson);
//2.导出时的excel名称
String dateString = String.valueOf(System.currentTimeMillis());
String fileName = "课程学生信息表" + dateString;
//3.查看结果集转成导出vo(转成对应的类型;要不然会报错)
List<CourseExcel> CourseExcelList = new ListUtils<CourseExcel>().copyProperties(list,CourseExcel.class);
//4.工具类导出excel
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;
/**
* 课程
* @author qzz
*/
@Data
public class CourseVo{
/**
* 课程id
*/
private Integer id;
/**
* 课程姓名
*/
private String name;
/**
* 老师
*/
private TeacherExcel teacherExcel;
/**
* 学生列表
*/
private List<StudentExcel> studentExcelList;
}
CourseMapper创建查询方法:
public interface CourseMapper extends MyMapper<Course> {
/**
* 获取课程导出列表
* @param CourseRequestJson
* @return
*/
List<CourseVo> getCourseExportList(CourseRequestJson CourseRequestJson);
/**
* 根据课程id获取学生列表信息
* @param courseId
* @return
*/
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(1对1) 这个标签对应BaseResultMap_1中的teacherExcel成员变量的属性-->
<association property="teacherExcel" javaType="com.example.exceldemo.excel.TeacherExcel">
<result property="name" column="teacher_name"></result>
</association>
<!--collection(1对多) 代表集合-->
<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;
/**
* 公司
* @author qzz
*/
@Data
public class CompanyExcel implements Serializable {
/**
* 公司名称
*/
@Excel(name = "公司名称")
private String name;
/**
* width和height是导出是图片的宽度和高度
* type:导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本
* imageType 导出类型;1:从file读取;2:是从数据库中读取,默认是文件;同样导入也是一样的
* savePath:保存的路径(右击保存图片的文件夹,选Copy Path就行,这里是存在static下的imgs中)
*/
@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;
/**
* 配置访问自定义的资源访问路径
* @author Administrator
*
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${upload.dir}")
private String filePath;
/**
* 添加静态资源映射路径,images,css、js等都放在classpath下的static中
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
/**
* addResourceHandler 指的是对外暴露的访问路径
* addResourceLocations 指的是文件配置的目录
*/
//文件上传路径映射
registry.addResourceHandler("/company/**").addResourceLocations("file:"+filePath);
}
}
3.3.2 在控制器类中添加一个导出方法
CompanyController:
/**
* 导出excel,含图片
* @param response
*/
@RequestMapping("/exportExcel")
public void exportExcel(HttpServletResponse response){
try {
//1.查询出要导出的数据(图片路径为上传图片全路径)
List<Company> companyList = companyService.findAll();
List<CompanyExcel> companyExcelList = new ListUtils<CompanyExcel>().copyProperties(companyList, CompanyExcel.class);
//2.导出时的excel名称
String dateString = String.valueOf(System.currentTimeMillis());
String fileName = "公司信息" + dateString;
//3.工具类导出Excel
ExportParams params = new ExportParams();
params.setTitle("公司信息列表");
params.setSheetName("公司信息");
//需要设置type=ExcelType.HSSF,不然图片不显示
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:
/**
* 批量导入,含图片
* @param file
* @return
*/
@RequestMapping("/upload")
public Result importExcel(@RequestParam("file") MultipartFile file,HttpServletRequest request){
try{
String originalFileName = file.getOriginalFilename();
//excel导入
List<CompanyExcel> companyExcelList = ExcelUtils.importExcel(file,1,1,CompanyExcel.class);
//调用getImgName方法获取图片名
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;
/**
* 文件转存
* @param companyList
*/
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 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("."));
//存加密后的uuid+后缀作为存到path里的文件名
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导入成功!!!
四、完整代码
点击此处进行下载