EasyExcel实现Excel的导入导出
阅读目录
前言:最近练习简单项目过程中使用Excel的导入导出较多,特来记录一下相关代码逻辑
准备工作
1. 首先在pom.xml文件中引入相关依赖
其他依赖若有需要可以引入
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mybatis-plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!--mybatis-plus 代码模板生成器引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--阿里巴巴Easy Excel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- xml -->
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2. 在controller层中创建一个AdminAttendanceDictController.java文件
相关代码如下
package com.xxx.student.core.controller.dict;
import com.alibaba.excel.EasyExcel;
import com.sun.deploy.net.URLEncoder;
import com.xxx.student.common.exception.BusinessException;
import com.xxx.student.common.result.R;
import com.xxx.student.common.result.ResponseEnum;
import com.xxx.student.core.pojo.dto.AttendanceExcelDTO;
import com.xxx.student.core.service.AttendanceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Li
*/
@Api(tags = "数据字典管理")
@RestController
@Slf4j
@CrossOrigin
@RequestMapping("/admin/core/dictAttendance")
public class AdminAttendanceDictController {
@Resource
private AttendanceService attendanceService;
@ApiOperation("Excel日常考勤信息数据的导出")
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyExcel没有关系
String fileName = URLEncoder
.encode("日常考勤数据字典列表", "UTF-8").replaceAll("\\+", "%20");
response
.setHeader("Content-disposition", "attachment;filename*=utf-8''" +
fileName + ".xlsx");
EasyExcel
.write(response.getOutputStream(), AttendanceExcelDTO.class)
.sheet("数据字典")
.doWrite(attendanceService.listDictData());
}
@ApiOperation("Excel日常考勤信息数据的批量导入")
@PostMapping("/import")
public R batchImport(@ApiParam(value = "Excel数据字典文件", required = true)
@RequestParam("file") MultipartFile file) {
try {
InputStream inputStream = file.getInputStream();
attendanceService.importData(inputStream);
return R.ok().message("数据字典批量导入成功");
} catch (Exception e) {
// 文件上传错误
throw new BusinessException(ResponseEnum.UPLOAD_ERROR, e);
}
}
}
3.我们注意到AdminAttendanceExcelDto.java还未定义
所以我们需要新建一个AdminAttendanceExcelDto.java文件,代码如下
package com.xxx.student.core.pojo.dto;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.xxx.student.core.util.LocalDateTimeConverter;
import lombok.Data;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author lsh
* @since 2021-07-25
*/
@Data
@ContentRowHeight(15) //设置行高
@HeadRowHeight(20) //设置表头高度
@ColumnWidth(20) //设置列宽
public class AttendanceExcelDTO {
@ExcelProperty(index = 0,value = "学生学号")
private Long studentNo;
@ExcelProperty(index = 1,value = "考勤时间",converter = LocalDateTimeConverter.class)
private LocalDateTime attendanceTime;
@ExcelProperty(index = 2,value = "考勤地点")
private String attendanceLocation;
@ExcelProperty(index = 3,value = "宿舍号")
private Integer dormitoryNo;
@ExcelIgnore
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty(value = "创建时间")
private LocalDateTime createTime;
@ExcelIgnore
@ExcelProperty(value = "更新时间")
private LocalDateTime updateTime;
@ExcelIgnore
@ExcelProperty(value = "逻辑删除")
private Boolean deleted;
}
4. 这里我们又使用到了一个用于转换LocalDateTime的一个工具类
package com.xxx.student.core.util;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 自定义转换器,转换LocalDateTime
* @author Li
*/
public class LocalDateTimeConverter implements Converter<LocalDateTime> {
@Override
public Class<LocalDateTime> supportJavaTypeKey() {
return LocalDateTime.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Override
public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}
5.我们还需要创建一个AttendanceExcelDictDTOListener.java作为监听器来帮助我们检测导入的情况
继承 AnalysisEventListener<AttendanceExcelDTO>,泛型为我们要实现的ExcelDTO对象
package com.xxx.student.core.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.CollectionUtils;
import com.xxx.student.core.mapper.AttendanceMapper;
import com.xxx.student.core.pojo.dto.AttendanceExcelDTO;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
/**
* @author Li
*/
@Slf4j
@NoArgsConstructor
public class AttendanceExcelDictDTOListener extends AnalysisEventListener<AttendanceExcelDTO> {
// 数据列表
ArrayList<AttendanceExcelDTO> list = new ArrayList<>();
private AttendanceMapper attendanceMapper;
// 每隔10条数据批量存入一次数据
static final int BATCH_COUNT = 5;
// 每次创建Listener的时候需要把spring管理的类传进来
public AttendanceExcelDictDTOListener(AttendanceMapper attendanceMapper){
this.attendanceMapper = attendanceMapper;
}
@Override
public void invoke(AttendanceExcelDTO data, AnalysisContext context) {
log.info("解析到一条记录:{}", data);
// 将数据存入到数据列表
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if(list.size() >= BATCH_COUNT){
// 调用mapper层的save方法
savaData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context .
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 当剩余的记录数不足BATCH_COUNT时,最终一次性存储数据
savaData();
log.info("所有数据解析完成");
}
private void savaData(){
log.info("{} 条数据存储到数据库", list.size());
// 调用mapper层的save方法 SAVE list对象
if(!CollectionUtils.isEmpty(list)) {
attendanceMapper.insertBatch(list);
}
//TODO
log.info("{} 条数据被存储到数据库成功", list.size());
}
}
6.service层创建一个名为AttendanceService的接口文件部分代码如下
package com.xxx.student.core.service;
import com.xxx.student.core.pojo.dto.AttendanceExcelDTO;
import com.xxx.student.core.pojo.entity.Attendance;
import com.baomidou.mybatisplus.extension.service.IService;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* <p>
* 服务类
* </p>
*
* @author lsh
*/
public interface AttendanceService extends IService<Attendance> {
/**
*
* @return Excel导出
*/
List<AttendanceExcelDTO> listDictData();
/**
*
* @param inputStream Excel导入
*/
void importData(InputStream inputStream);
}
7.ServiceImpl部分代码如下
@Slf4j
@Service
public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attendance> implements AttendanceService {
private final QueryWrapper<Attendance> attendanceQueryWrapper = new QueryWrapper<>();
@Resource
private AttendanceMapper attendanceMapper;
@Override
public List<AttendanceExcelDTO> listDictData() {
List<Attendance> list = baseMapper.selectList(null);
//创建ExcelDictDTO列表,将Dict列表转换成ExcelDictDTO列表
ArrayList<AttendanceExcelDTO> excelDictDTOList = new ArrayList<>(list.size());
// 遍历dict列表
list.forEach(dict -> {
AttendanceExcelDTO attendanceExcelDTO = new AttendanceExcelDTO();
BeanUtils.copyProperties(dict, attendanceExcelDTO);
excelDictDTOList.add(attendanceExcelDTO);
});
return excelDictDTOList;
}
@Override
public void importData(InputStream inputStream) {
EasyExcel
.read(inputStream, AttendanceExcelDTO.class, new AttendanceExcelDictDTOListener(baseMapper))
.excelType(ExcelTypeEnum.XLSX)
.sheet()
.doRead();
log.info("Excel导入成功");
}
}
8.mapper层
/**
*
* @param list 批量插入
*/
void insertBatch(ArrayList<AttendanceExcelDTO> list);
9.mapper.xml文件
<insert id="insertBatch">
INSERT INTO `student_teacher_info`.`attendance`(`student_No`, `attendance_Time`, `attendance_Location`, `dormitory_No`)
VALUES<foreach collection="list" separator="," item="item" index="index">(#{item.studentNo},
#{item.attendanceTime},
#{item.attendanceLocation},
#{item.dormitoryNo})</foreach>
</insert>
10.实体类Attendance
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Attendance对象", description="日常考勤")
public class Attendance implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "学生学号")
@TableId(value = "student_No", type = IdType.INPUT) // 用户输入
private Long studentNo;
@JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = " 考勤时间")
@TableField("attendance_Time")
private LocalDateTime attendanceTime;
@ApiModelProperty(value = "考勤地点")
@TableField("attendance_Location")
private String attendanceLocation;
@ApiModelProperty(value = "宿舍号")
@TableField("dormitory_No")
private Integer dormitoryNo;
@ApiModelProperty(value = "创建时间",example = " ")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间",example = " ")
private LocalDateTime updateTime;
@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
@TableField("is_deleted")
@TableLogic
private Boolean deleted;
}
至此:导入导出模块基本完成
__EOF__

本文作者:Hui Li
本文链接:https://www.cnblogs.com/leedev-blog/p/15212016.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/leedev-blog/p/15212016.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!