EasyExcel和Echarts的碰撞

题外话:最近在学习EasyExcel知识的时候,就想着如何把Excel数据导入我们的数据库里面,然后再如何把Excel表格里面的数据再更加直观的在网页中显示出来,这时候脑海里就突然想试试Echarts,于是就开始了这个小实验

附上语雀笔记,这里有完整的代码介绍:https://www.yuque.com/xiaoqian-paazl/pwc7tt/etdca557p256wzre?singleDoc#


先附上我的实现效果叭✿✿ヽ(°▽°)ノ✿

Excel表格内的数据

有一个小Bug:就是Excel的时间戳没传上,导致时间为null

1、了解EasyExcel

在开始小实验之前,我们要先了解一下什么是EasyExcel。在我们处理Excel表格的时候,Apache先给了我们一个工具来使用就是poi,这个poi写起来有点繁杂,甚至繁琐,还占用内存,容易OOM,这时候阿里就基于POI开发出EasyExcel,用起来还是很方便的(还有一个hutool-poi也可以处理,但是我还没仔细学习)

具体使用可以参考EasyExcel官方文档:https://easyexcel.opensource.alibaba.com/docs/current/

附上Echarts文档:https://echarts.apache.org/handbook/zh/basics/download/

1、前端主要核心代码

通过ajax发送请求,用户点击上传按钮后,前端JavaScript代码将文件通过AJAX请求发送到后端的 /api/upload 接口,这是进行将前端页面上的文件上传表单选择要导入的Excel文件。请求处理 :前端使用jQuery的AJAX功能,将MultipartFile类型的文件数据发送到后端。

text
复制代码
$.ajax({ url: '/api/upload', type: 'POST', data: formData, contentType: false, processData: false, success: function(response) { document.getElementById('message').textContent = '上传成功!'; document.getElementById('message').style.color = 'green'; // 上传成功后重新加载图表 loadCharts(); }, error: function() { document.getElementById('message').textContent = '上传失败,请重试'; document.getElementById('message').style.color = 'red'; } }); });
plain
复制代码
// 加载图表数据 function loadCharts() { $.ajax({ url: '/api/employees', type: 'GET', success: function(employees) { // 处理工资图表 updateSalaryChart(employees); // 处理员工统计图表 updateCountChart(employees); }, error: function() { console.error('获取员工数据失败'); } }); }

这里就是Echarts知识

plain
复制代码
// 更新工资图表 function updateSalaryChart(employees) { const names = employees.map(emp => emp.name); const salaries = employees.map(emp => emp.salary); salaryChart.setOption({ title: { text: '员工工资对比', left: 'center' }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, xAxis: { type: 'category', data: names, axisLabel: { rotate: 45 } }, yAxis: { type: 'value', name: '工资' }, series: [{ data: salaries, type: 'bar', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ {offset: 0, color: '#83bff6'}, {offset: 0.5, color: '#188df0'}, {offset: 1, color: '#188df0'} ]) } }] }); }
plain
复制代码
// 更新员工薪资图表 function updateCountChart(employees) { const names = employees.map(emp => emp.name); const salaries = employees.map(emp => emp.salary); countChart.setOption({ title: { text: '员工薪资折线图', left: 'center' }, tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: names }, yAxis: { type: 'value', name: '工资' }, series: [{ data: salaries, type: 'line', smooth: true, itemStyle: { color: '#ff7f50' }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ {offset: 0, color: 'rgba(255,127,80,0.8)'}, {offset: 1, color: 'rgba(255,127,80,0.1)'} ]) } }] }); }

2、后端代码

2.1 Controller层

plain
复制代码
@RestController @RequestMapping("/api") public class MyController { @GetMapping("/") public String index() { return "index"; // 指向resources/static/index.html } @Resource private EmployeeService employeeService; @PostMapping("/upload") @ResponseBody public String upload(MultipartFile file) throws IOException { EasyExcel.read(file.getInputStream(), Employee.class, new EmployeeListener(employeeService)).sheet().doRead(); return "success"; } // 获取所有员工数据,用于ECharts展示 @GetMapping("/employees") public List<Employee> getAllEmployees() { return employeeService.getAllEmployees(); } }

当我们在前端点击上传文件之后,ajax发送请求,调用upload接口,这时候就初始化EasyExcel :控制器调用 EasyExcel.read() 方法,传入以下参数:

○ 文件输入流( file.getInputStream() )

○ 目标实体类类型( Employee.class )

○ 自定义监听器实例( new EmployeeListener(employeeService) )

在创建监听器时,通过构造函数注入了 EmployeeService 实例,以便后续进行数据库操作

2.2 Service层

plain
复制代码
public interface EmployeeService extends IService<Employee> { /** * 批量导入员工数据 * @param employees 员工数据列表 * @return 是否导入成功 */ boolean importEmployees(List<Employee> employees); /** * 获取所有员工数据 * @return 员工数据列表 */ List<Employee> getAllEmployees(); }
plain
复制代码
@Service public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService{ /** * 批量导入员工数据 * @param employees 员工数据列表 * @return 是否导入成功 */ @Override @Transactional(rollbackFor = Exception.class) public boolean importEmployees(List<Employee> employees) { if (employees == null || employees.isEmpty()) { return false; } // 使用MyBatis-Plus的批量插入方法,提高插入效率 return this.saveBatch(employees, 1000); // 每批处理1000条数据 } /** * 获取所有员工数据 * @return 员工数据列表 */ @Override public List<Employee> getAllEmployees() { return this.list(); } }

2.3📕监听器设置(重点!!)

  • 逐行读取 :EasyExcel逐行读取Excel数据,每读取一行就调用 EmployeeListener 的 invoke 方法。
  • 数据收集 : invoke 方法将解析后的Employee对象添加到临时集合 employeeArrayList 中。
  • 批量处理 :当集合大小达到设定的批处理阈值(BATCH_SIZE=100)时,触发批量插入操作:
    • 调用 employeeService.importEmployees(employeeArrayList) 方法
    • 清空集合,为下一批数据做准备
plain
复制代码
/** * 自定义监听器读数据 */ public class EmployeeListener implements ReadListener<Employee> { private static final int BATCH_SIZE = 100; private ArrayList<Employee> employeeArrayList = new ArrayList<>(BATCH_SIZE); private EmployeeService employeeService; // 无参构造函数,Spring需要 public EmployeeListener() { // 实际使用中会通过有参构造函数注入employeeService } // 有参构造函数,用于注入EmployeeService public EmployeeListener(EmployeeService employeeService) { this.employeeService = employeeService; } //invoke 是每读一行数据,都会调用这个方法 @Override public void invoke(Employee employee, AnalysisContext analysisContext) { //将读取到的这一行添加到集合 employeeArrayList.add(employee); //判断是否达到缓存量 if (employeeArrayList.size() >= BATCH_SIZE) { //调用service层的批量导入方法 employeeService.importEmployees(employeeArrayList); employeeArrayList = new ArrayList<>(BATCH_SIZE); } } // 读完整个excel之后,都会调用这个方法 @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { if (employeeArrayList.size() > 0) { //调用service层的批量导入方法 employeeService.importEmployees(employeeArrayList); employeeArrayList = new ArrayList<>(BATCH_SIZE); } } }

2.4 domain层

ExcelProperty是EasyExcel中用于匹配excel和实体类的匹配,也就是我们Excel表格第一行的各列表头

plain
复制代码
/** * * @TableName employee */ @TableName(value ="employee") @Data public class Employee { /** * 员工编号 */ @ExcelProperty("员工编号") @TableId(type = IdType.AUTO) private Integer id; /** * 员工姓名 */ @ExcelProperty("员工姓名") private String name; /** * 入职时间 */ @ExcelProperty("入职时间") private Date date; /** * 员工工资 */ @ExcelProperty("员工工资") private BigDecimal salary; }

3、总结

这是我第一次在编程导航写的文章,可能说的不太清楚,具体学习还得参考官方文档,这是我突然想到的一个小实验,利用EasyExcel来操控Excel,然后将Excel数据保存到数据库,以及如何将Excel表格里面的数据更好地显示到前端界面上,这里过程中还使用了ai辅助帮忙,总之有一些小小的收获,把自己的小想法也分享给大家。Thanks♪(・ω・)ノ

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
Leu_08
作者分享
深航26信息技术春招(一二面汇总)
4
Day 2 ✅ 今天做了:海投100家+面试题+LeetCode5道 ⏰ 明天计划:早上继续海投来约面进行训练,八股背诵,项目优化实现然后做LeetCode 📚 今日感悟:今天的海投大部分已读不回,有十几家家要了简历,然后有一个离谱的一直问我能不能接受加班,说他们家一直996,长期加班出差,我答应了毕竟就是为了海投约面,反正最后也不去这种拿offer,哎,晚上跟家里人聊天,他们好像只认可央国企,我一说去找工作上班,他们就说我这个就是假公司迟早被裁员,我本来就很烦了,我也知道央国企的好处,但是说了好几次不想去,而且现在人家也不要我啊,一天天说这个,不管是什么企业,只要能给我一直发工资的企业就是好企业呀,服了
3
Day 1 ✅ 今天做了:海投、八股、LeetCode 5道、Stream流和Lambda ⏰ 明天计划:早上海投、八股、LeetCode 5道、完善项目 📚 今日感悟:今天跟硕士同学交流了一下,发现大家都在焦虑,哎,今天进了三家人才库😭,做了两家测评,目前还没有一家面试,可能投的太少了(目前就投了20家),明天要提高效率了,想约小厂来试试自己的面试水平
3
Agent AI 超级智能体企业级实战(26年必学) - 基于Redis存储持久化(SpringAI1.1.2版本)
4
Day 11 ✅ 今天做了:Redis实战篇--短信验证学完了,整理了笔记,把途中遇到的bug总结了一下,顺便用ai帮助扩展了知识点 ⏰ 明天计划:Redis实战篇--商户查询缓存学习,面试题看一会,SpringBoot知识点 📚 今日感悟:总体效率跟不上啊,有时候自己太过于对细节追究太深,导致许多没学过的知识涌过来的时候就吃不消,还是得慢慢来,今天对有些方法有了很深的体会,继续加油ヾ(◍°∇°◍)ノ゙
2
下载 APP