提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

423 lines
12 KiB

  1. # EasyExcel学习
  2. ## 1.环境搭建
  3. ```
  4. <dependency>
  5. <groupId>com.alibaba</groupId>
  6. <artifactId>easyexcel</artifactId>
  7. <version>3.1.2</version>
  8. </dependency>
  9. ```
  10. **项目结构**:
  11. ![1762417616189](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1762417616189.png)
  12. ## 2.本地模拟实现
  13. ### 1.简单写入excel
  14. 第一步:首先创建实体类Employee
  15. > @ExcelProperty(value = "员工id",index = 0)注解就是代表的excel表的表头的每一列的名字
  16. ```java
  17. @Data@Data
  18. @NoArgsConstructor
  19. @AllArgsConstructor
  20. public class Employee {
  21. @ExcelProperty(value = "员工id",index = 0)
  22. private Integer id;
  23. @ExcelProperty(value = "员工姓名",index = 1)
  24. private String name;
  25. @ExcelProperty(value = "入职日期",index = 2)
  26. private Date date;
  27. @ExcelProperty(value = "员工工资",index = 3)
  28. private Double salary;
  29. }
  30. ```
  31. 第二步:创建工具类:用于获取当前文件夹的路径
  32. ``` java
  33. public class TestFileUtil {
  34. public static String getPath(){
  35. return TestFileUtil.class.getResource("/").getPath().replace("classes/", "");
  36. }
  37. public static void main(String[] args) {
  38. System.out.println(getPath());
  39. }
  40. }
  41. ```
  42. 第三步:写操作主要代码:
  43. ```java
  44. public class SimpleWrite {
  45. //准备测试数据的方法
  46. private List<Employee> data(int count) {
  47. List<Employee> list = ListUtils.newArrayList();
  48. for (int i = 1; i < count; i++) {
  49. list.add(new Employee(i,"测试数据1"+i,new Date(),6.6*i));
  50. }
  51. return list;
  52. }
  53. //快速入门写数据
  54. @Test
  55. public void write(){
  56. String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
  57. // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
  58. EasyExcel.write(fileName, Employee.class).sheet("模板").doWrite(data(10));
  59. }
  60. }
  61. ```
  62. ### 2.简单读出excel
  63. 只需要一个方法,就可以读出excel表格信息并在控制台打印
  64. ```java
  65. //快速入门读数据
  66. public class SimpleReader {
  67. @Test
  68. public void read(){
  69. String fileName = TestFileUtil.getPath() + "simpleWrite1762401148464.xlsx";
  70. // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
  71. // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
  72. EasyExcel.read(fileName, Employee.class, new PageReadListener<Employee>(dataList -> {
  73. for (Employee demoData : dataList) {
  74. System.out.println(demoData);
  75. }
  76. })).sheet().doRead();
  77. }
  78. }
  79. ```
  80. ### 3.批量写入excel
  81. ``` java
  82. public class ManyWrite {
  83. //准备测试数据的方法
  84. private List<Employee> data(int count) {
  85. List<Employee> list = ListUtils.newArrayList();
  86. for (int i = 1; i < count; i++) {
  87. list.add(new Employee(i,"测试数据1"+i,new Date(),6.6*i));
  88. }
  89. return list;
  90. }
  91. //批量写数据
  92. @Test
  93. public void write(){
  94. // 方法2: 如果写到不同的sheet 同一个对象
  95. String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
  96. // 这里 指定文件
  97. try (ExcelWriter excelWriter = EasyExcel.write(fileName, Employee.class).build()) {
  98. WriteSheet writeSheet = EasyExcel.writerSheet( "测试数据").build();
  99. long t1 =System.currentTimeMillis();
  100. // 去调用写入
  101. for (int i = 0; i < 100; i++) {
  102. // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
  103. List<Employee> data = data(10000);
  104. excelWriter.write(data, writeSheet);
  105. }
  106. long t2 =System.currentTimeMillis();
  107. System.out.println("耗时:"+(t2-t1));
  108. }
  109. }
  110. }
  111. ```
  112. ### 4.读海量数据
  113. 读海量数据需要自定义监听器EmployeeListener
  114. ```java
  115. //自定义监听器读数据
  116. public class EmployeeListener implements ReadListener<Employee> {
  117. private ArrayList<Employee> list = new ArrayList<>();
  118. private int count = 100;
  119. private EmployeeDao dao;
  120. public EmployeeListener(EmployeeDao dao) {
  121. this.dao = dao;
  122. }
  123. //每读完一行数据,就会调用此方法
  124. @Override
  125. public void invoke(Employee employee, AnalysisContext analysisContext) {
  126. //将读取的一行数据保存到list中
  127. list.add(employee);
  128. //判断是不是到达缓存量了
  129. if(list.size()>=100){
  130. //操作数据库
  131. dao.save(list);
  132. list = new ArrayList<>(count);
  133. }
  134. }
  135. //读完整个excel之后后调用此方法
  136. @Override
  137. public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  138. if(list.size()>0){
  139. //操作数据库
  140. dao.save(list);
  141. list = new ArrayList<>(count);
  142. }
  143. }
  144. }
  145. ```
  146. 以下是Dao层模拟数据库操作
  147. ```java
  148. //模拟操作数据库
  149. public class EmployeeDao {
  150. public void save(List<Employee> list){
  151. System.out.println(list.size()+"模拟操作数据库");
  152. }
  153. }
  154. ```
  155. ```java
  156. //读海量数据
  157. public class ManyRead {
  158. @Test
  159. public void read(){
  160. String fileName = TestFileUtil.getPath() + "repeatedWrite1762406295595.xlsx";
  161. ExcelReader reader = EasyExcel.read(fileName, Employee.class, new EmployeeListener(new EmployeeDao())).build();
  162. ReadSheet sheet = EasyExcel.readSheet().build();
  163. reader.read(sheet);
  164. }
  165. }
  166. ```
  167. ### 5.使用模板填充Excel表格
  168. 首先在模板中用{.}定义好每一列的名称对应什么
  169. ![1762415858999](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1762415858999.png)
  170. ``` java
  171. //练习填充数据
  172. public class FillWriter {
  173. //准备测试数据的方法
  174. private List<Employee> data(int count) {
  175. List<Employee> list = ListUtils.newArrayList();
  176. for (int i = 1; i < count; i++) {
  177. list.add(new Employee(i,"测试数据1"+i,new Date(),6.6*i));
  178. }
  179. return list;
  180. }
  181. @Test
  182. public void write(){
  183. String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx";
  184. String templateFileName = TestFileUtil.getPath() + "模板.xlsx";
  185. try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
  186. WriteSheet writeSheet = EasyExcel.writerSheet().build();
  187. long t1 = System.currentTimeMillis();
  188. for (int i = 0; i < 100; i++) {
  189. excelWriter.fill(data(10000), writeSheet);
  190. }
  191. long t2 = System.currentTimeMillis();
  192. System.out.println("耗时:"+(t2-t1));
  193. }
  194. }
  195. }
  196. ```
  197. ## 3.前后端端实现导入导出Excel
  198. **listener定义**:
  199. ```java
  200. package com.itheima.listener;
  201. import com.alibaba.excel.context.AnalysisContext;
  202. import com.alibaba.excel.read.listener.ReadListener;
  203. import com.itheima.domain.Employee;
  204. import com.itheima.service.EmployeeService;
  205. import java.util.ArrayList;
  206. import java.util.List;
  207. public class EmployeeListener implements ReadListener<Employee> {
  208. /**
  209. * 批量处理的数据量阈值
  210. */
  211. private int count = 10000;
  212. private EmployeeService dao ;
  213. /**
  214. * 临时存储读取的数据列表
  215. */
  216. private List<Employee> list = new ArrayList<>(count);
  217. public EmployeeListener(EmployeeService dao) {
  218. this.dao = dao;
  219. }
  220. /**
  221. * 处理每一行数据
  222. * 当读取到一行数据时会调用此方法
  223. * @param employee 当前行的员工数据
  224. * @param analysisContext 分析上下文
  225. */
  226. @Override
  227. public void invoke(Employee employee, AnalysisContext analysisContext) {
  228. list.add(employee);
  229. // 当累积的数据量达到阈值时,执行批量插入操作
  230. if(list.size()>=count){
  231. dao.addData(list);
  232. // 重新初始化列表,准备下一批数据
  233. list = new ArrayList<>(count);
  234. }
  235. }
  236. /**
  237. * 所有数据解析完成后的回调方法
  238. * 用于处理剩余未达到批量阈值的数据
  239. * @param analysisContext 分析上下文
  240. */
  241. @Override
  242. public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  243. // 处理最后一批未达到阈值的数据
  244. if(list.size()>0){
  245. dao.addData(list);
  246. }
  247. }
  248. }
  249. ```
  250. **pojo层:**
  251. pojo层和上面定义一致:
  252. ```
  253. @Data
  254. @NoArgsConstructor
  255. @AllArgsConstructor
  256. public class Employee {
  257. @ExcelProperty(value = "员工id",index = 0)
  258. private Integer id;
  259. @ExcelProperty(value = "员工姓名",index = 1)
  260. private String name;
  261. @ExcelProperty(value = "入职日期",index = 2)
  262. private Date date;
  263. @ExcelProperty(value = "员工工资",index = 3)
  264. private Double salary;
  265. }
  266. ```
  267. **controller层:**
  268. 主要是两个方法,一个上传,一个下载
  269. ```java
  270. @Controller
  271. @RequestMapping("/")
  272. public class MyController {
  273. @Autowired
  274. private EmployeeService service;
  275. @RequestMapping("/upload")
  276. @ResponseBody
  277. public void upload(MultipartFile file, HttpServletResponse response) throws IOException {
  278. long start = System.currentTimeMillis();
  279. EasyExcel.read(file.getInputStream(), Employee.class, new EmployeeListener(service)).sheet().doRead();
  280. long end = System.currentTimeMillis();
  281. response.setContentType("text/html;charset=utf-8");
  282. response.getWriter().print("上传成功,共用时:"+(end-start));
  283. }
  284. @RequestMapping("/download")
  285. public void download(HttpServletResponse response) throws IOException {
  286. // 这里注意 使用swagger 会导致各种问题,直接用浏览器或者用postman
  287. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  288. response.setCharacterEncoding("utf-8");
  289. // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
  290. String fileName = URLEncoder.encode("数据库中导出的数据", "UTF-8").replaceAll("\\+", "%20");
  291. response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
  292. ExcelWriter writer = EasyExcel.write(response.getOutputStream(), Employee.class).build();
  293. WriteSheet sheet = EasyExcel.writerSheet("数据").build();
  294. writer.write(service.getData(), sheet);
  295. writer.close();
  296. }
  297. }
  298. ```
  299. **Service层**
  300. ```java
  301. package com.itheima.service;
  302. import com.itheima.domain.Employee;
  303. import org.springframework.stereotype.Service;
  304. import java.util.List;
  305. public interface EmployeeService {
  306. public List<Employee> getData();
  307. public void addData(List<Employee> list);
  308. }
  309. ```
  310. ```java
  311. package com.itheima.service.impl;
  312. import com.itheima.domain.Employee;
  313. import com.itheima.mapper.EmployeeMapper;
  314. import com.itheima.service.EmployeeService;
  315. import org.springframework.beans.factory.annotation.Autowired;
  316. import org.springframework.stereotype.Service;
  317. import java.util.List;
  318. @Service
  319. public class EmployeeServiceImpl implements EmployeeService {
  320. @Autowired
  321. private EmployeeMapper dao;
  322. @Override
  323. public List<Employee> getData() {
  324. return dao.getData();
  325. }
  326. @Override
  327. public void addData(List<Employee> list) {
  328. dao.beathInsert(list);
  329. }
  330. }
  331. ```
  332. **Mapper层**
  333. ```java
  334. public interface EmployeeMapper {
  335. public void beathInsert(@Param("list") List<Employee> list);
  336. // @Select("select * from employee ")
  337. @Select("select * from employee")
  338. @ResultType(Employee.class)
  339. List<Employee> getData();
  340. }
  341. ```