文章目录
- 前言
- 技术栈
- 1、引入依赖
- 2、导入代码实现
- 3、导出代码实现
- 3.1、准备导出文件模板
- 3.2、导出代码实现
- 4、代码实现解释
- 5、常见问题
前言
这两天公司项目业务提出需求,要求在前端上传excel文件然后解析展示,因此写篇文章记录一下实现。
技术栈
springboot 2.6.6
1、引入依赖
1
2
3
4
5
6
7
8
9
10
11
12maven格式: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.14</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.14</version> </dependency>
2、导入代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96package com.nanmu.payment.controller; import com.nanmu.payment.common.MessageConst; import com.nanmu.payment.entity.Result; import com.nanmu.payment.pojo.OrderSetting; import com.nanmu.payment.service.OrderSettingService; import lombok.extern.slf4j.Slf4j; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.util.*; /** * @author * @description * @date 2022/12/22 **/ @RestController @RequestMapping("/ordersetting") @Slf4j public class OrderSettingController { /** * 上传预约设置的excel文件 * @param multipartFile * @return */ @RequestMapping("/upload") public Result upload(@RequestParam("excelFile") MultipartFile multipartFile) { log.info("[预约设置-上传]fileName:{},size:{}", multipartFile.getOriginalFilename(), multipartFile.getSize()); String filename = multipartFile.getOriginalFilename(); if(StringUtils.isEmpty(filename)){ return new Result(false,"缺少文件名"); } //1 抽取excel数据 poi try (InputStream is = multipartFile.getInputStream();) { Workbook workbook = null; //1.1构造workbook if(filename.endsWith(".xls")){ // excel 2003 workbook = new HSSFWorkbook(is); }else if(filename.endsWith(".xlsx")){ // excel 2007 workbook = new XSSFWorkbook(is); }else{ return new Result(false,"文件格式不正确,请检查重试"); } //1.2遍历取数据 List<OrderSetting> orderSettings = new ArrayList<>(); for (Sheet sheet : workbook) { // 第0行为表头,直接从第1行取 for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); Date date = null; int number = 0; try { date = null != row.getCell(0) ? row.getCell(0).getDateCellValue() : null; number = null != row.getCell(1) ? Double.valueOf(row.getCell(1).getNumericCellValue()).intValue() : 0; }catch (IllegalStateException|NumberFormatException e){ log.error("",e); return new Result(false,String.format("数据格式错误,%s 第%d行",sheet.getSheetName(),row.getRowNum()+1)); } if(null == date ){ return new Result(false,String.format("缺少必填数据,%s 第%d行",sheet.getSheetName(),row.getRowNum()+1)); } //构造OrderSetting orderSettings.add(new OrderSetting(date,number)); } } log.info("[预约设置-上传]解析成功,result:{}",orderSettings); //2 调用service层 //调用service层代码去掉 return new Result(true,MessageConst.IMPORT_ORDERSETTING_SUCCESS); } catch (RuntimeException|IOException e) { log.info("",e); return new Result(false, MessageConst.IMPORT_ORDERSETTING_FAIL); } } }
3、导出代码实现
3.1、准备导出文件模板
将导出文件模板放入resources包下面(如下图黄框所示)
文件内容如下图所示:
3.2、导出代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114package com.nanmu.payment.controller; import com.nanmu.payment.common.MessageConst; import com.nanmu.payment.entity.Result; import com.nanmu.payment.service.MemberService; import com.nanmu.payment.service.OrderService; import com.nanmu.payment.service.ReportService; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigDecimal; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; /** * 数据报告控制器 */ @RestController @RequestMapping("/report") @Slf4j public class ReportController { @Autowired private ReportService reportService; /** * 导出运营数据报表 * * @return */ @GetMapping("exportBusinessReport") public Result exportBusinessReport(HttpServletResponse response) { log.info("[导出运营数据报表]开始"); try { //1-调用service层获取数据 Map<String, Object> reportData = reportService.getBusinessReportData(); log.debug("[导出运营数据报表]rpc rsp:{}", reportData); // 取出返回结果 String reportDate = (String) reportData.get("reportDate"); // 获取会员相关数据 Long todayNewMember = (Long) reportData.get("todayNewMember"); Long thisWeekNewMember = (Long) reportData.get("thisWeekNewMember"); Long thisMonthNewMember = (Long) reportData.get("thisMonthNewMember"); Long totalMember = (Long) reportData.get("totalMember"); // 获取预约相关数据 Long todayOrderNumber = (Long) reportData.get("todayOrderNumber"); Long thisWeekOrderNumber = (Long) reportData.get("thisWeekOrderNumber"); Long thisMonthOrderNumber = (Long) reportData.get("thisMonthOrderNumber"); // 获取到诊相关数据 Long todayVisitsNumber = (Long) reportData.get("todayVisitsNumber"); Long thisWeekVisitsNumber = (Long) reportData.get("thisWeekVisitsNumber"); Long thisMonthVisitsNumber = (Long) reportData.get("thisMonthVisitsNumber"); // 获取套餐数据 List<Map> hotSetmeal = (List<Map>) reportData.get("hotSetmeal"); //2-读取模板构造workBook try (InputStream is = this.getClass().getClassLoader().getResourceAsStream("report_template.xlsx"); Workbook workbook = new XSSFWorkbook(is); OutputStream os = response.getOutputStream()) { Sheet sheet = workbook.getSheetAt(0); //3-填入数据 //日期 sheet.getRow(2).getCell(5).setCellValue(reportDate); //会员统计数据 sheet.getRow(4).getCell(5).setCellValue(todayNewMember); sheet.getRow(4).getCell(7).setCellValue(totalMember); sheet.getRow(5).getCell(5).setCellValue(thisWeekNewMember); sheet.getRow(5).getCell(7).setCellValue(thisMonthNewMember); //预约到诊数据统计 sheet.getRow(7).getCell(5).setCellValue(todayOrderNumber); sheet.getRow(7).getCell(7).setCellValue(todayVisitsNumber); sheet.getRow(8).getCell(5).setCellValue(thisWeekOrderNumber); sheet.getRow(8).getCell(7).setCellValue(thisWeekVisitsNumber); sheet.getRow(9).getCell(5).setCellValue(thisMonthOrderNumber); sheet.getRow(9).getCell(7).setCellValue(thisMonthVisitsNumber); //热门套餐 int rowNum = 12; for (Map setMeal : hotSetmeal) { Row row = sheet.getRow(rowNum); row.getCell(4).setCellValue((String) setMeal.get("name")); row.getCell(5).setCellValue((Long) setMeal.get("setmeal_count")); row.getCell(6).setCellValue(((BigDecimal) setMeal.get("proportion")).doubleValue()); rowNum++; } //4-写入网络输出流 //写入返回流 response.setContentType("application/vnd.ms-excel"); response.setHeader("content-Disposition", "attachment;fileName=" + reportDate + "_report.xlsx"); workbook.write(os); return null; } catch (IOException e) { // 不会出现/无法处理的受检异常,转换为运行时异常 throw new RuntimeException(e); } } catch (RuntimeException e) { log.error("", e); return new Result(false, MessageConst.ACTION_FAIL); } } }
4、代码实现解释
- 导入前端上传的文件不进行存储,直接读取之后将内容存入数据库;
- 导入支持.xls和.xlsx两种格式excel文件;
- 导入文件行数不易过大,过大会出现内存泄漏等问题(后面再出关于百万行数据读取、导出的方案);
- 导出文件使用模板,这样便于写入,同样不支持数据行过多;
- 导出直接返回.xlsx格式excel文件给前端。
5、常见问题
-
读取文件失败,提示信息:The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
分析及解决方案为:经分析发现是HSSFWorkbook不支持读取.xlsx,改为XSSFWorkbook即可。 -
内存溢出问题,提示信息:java.lang.OutOfMemoryError: Java heap space
at jdk.internal.reflect.GeneratedConstructorAccessor39.newInstance(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[na:na]
at org.apache.xmlbeans.impl.schema.SchemaTypeImpl.createUnattachedNode(SchemaTypeImpl.java:1934) ~[xmlbeans-2.6.0.jar:na]
at org.apache.xmlbeans.impl.schema.SchemaTypeImpl.createElementType(SchemaTypeImpl.java:1051) ~[xmlbeans-2.6.0.jar:na]
at org.apache.xmlbeans.impl.values.XmlObjectBase.create_element_user(XmlObjectBase.java:938) ~[xmlbeans-2.6.0.jar:na]
at org.apache.xmlbeans.impl.store.Xobj.getUser(Xobj.java:1675) ~[xmlbeans-2.6.0.jar:na]
at org.apache.xmlbeans.impl.store.Xobj.find_all_element_users(Xobj.java:2098) ~[xmlbeans-2.6.0.jar:na]
at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTRowImpl.getCArray(Unknown Source) ~[poi-ooxml-schemas-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFRow.(XSSFRow.java:73) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFSheet.initRows(XSSFSheet.java:215) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFSheet.read(XSSFSheet.java:178) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFSheet.onDocumentRead(XSSFSheet.java:165) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFWorkbook.parseSheet(XSSFWorkbook.java:417) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFWorkbook.onDocumentRead(XSSFWorkbook.java:382) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:178) ~[poi-ooxml-3.14.jar:3.14]
at org.apache.poi.xssf.usermodel.XSSFWorkbook.(XSSFWorkbook.java:279) ~[poi-ooxml-3.14.jar:3.14]
分析及解决方案为:经分析发现是导入、导出数据行数太多,目前建议业务分批导出,后续出百万行数据读取、导出的方案。
最后
以上就是优秀猫咪最近收集整理的关于Apache POI导入导出excel文件实战的全部内容,更多相关Apache内容请搜索靠谱客的其他文章。
发表评论 取消回复