写在前面
前后端分离:前端VUE+ELEMENT,后端springboot。
excel上传至后端
设置后端接口
先不做处理,收到文件就回复一个导入成功。(RespBean是为了规范,自己封装的一个用于统一回复格式的类,这里改成String也是一样的)
@PostMapping("/import")
public RespBean importExcel(MultipartFile file){
System.out.println("收到文件");
return RespBean.ok("导入成功");
}
前端使用upload组件
上传时采用的是element的upload组件,不仅仅是element,只要是成熟的Vue组件库肯定都有upload组件,所以这个不所谓,使用对应upload组件即可。具体使用参照官网:element upload组件
<el-upload action="/api/order/import"
:show-file-list="false"
:on-success="hanleSuccess"
accept=".xls">
<el-button type="primary">导入订单</el-button>
</el-upload>
action指定后端地址;accept指定允许的导入文件的格式;show-file-list置为false,那么成功导入后,页面上不会显示已导入的文件列表(跟代码逻辑无关,只是修改一下视觉效果);hanleSuccess是成功时回调方法,我用来展示后端返回的信息。当然,这几个属性最重要的还是action,其它根据自己开发需要,参照一下官网,自定义就好了。
至此就导入成功了,接下来时excel的解析
解析excel
使用POI:Apache POI提供API给Java对Microsoft Office格式档案读和写的功能。
导入依赖
<!--解析excel-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>RELEASE</version>
</dependency>
创建一个解析excel的工具类,将前端得到的file作为参数:
//controller
@PostMapping("/import")
public RespBean importExcel(MultipartFile file){
// System.out.println("收到文件");
List<Order> orders = ExcelUtils.excelToOrder(file);
return RespBean.ok("导入成功");
}
下面是工具类的代码:(注释写里面了,节省空间)
简单来说就是:先拿到整个表的对象,再拿到单张sheet的对象,再拿到每一行的对象,再分别取出一行中每一个单元格的值。
public class ExcelUtils {
public static List<Order> excelToOrder(MultipartFile file){
// 新建一个list,用于存放解析结果
List<Order> list=new ArrayList<>();
try {
// 拿到对象
HSSFWorkbook workbook = new HSSFWorkbook(file.getInputStream());
// 一个excel可能有多个sheet,所以遍历sheet
for (int i = 0; i <workbook.getNumberOfSheets() ; i++) {
// 拿到sheet对象
HSSFSheet sheet = workbook.getSheetAt(i);
// System.out.println("行数"+sheet.getPhysicalNumberOfRows());
// 遍历每一行
for (int j = 0; j <sheet.getPhysicalNumberOfRows() ; j++) {
//略过首行,即标题行
if(j==0){
continue;
}
// 拿到行的对象
HSSFRow row = sheet.getRow(j);
// 即使你的表格中没有空行,但也可能会被解析出一些空行,所以我们略过空行,否则会报错
if (row==null){
continue;
}
// System.out.println("列数"+row.getPhysicalNumberOfCells());
// new一个自己的实体类的对象,方便一会儿将解析出来的值存放进去
Order order = new Order();
// 注意下面这种方法是最简单但是也最死板的方法,就是固定excel的表头
// 每一列只能是对应的字段。一旦excel的某两列相互替换一下位置
// 那么写入order的值就乱了
// 遍历一行中的每一个单元格
for (int k = 0; k <row.getPhysicalNumberOfCells() ; k++) {
// 写固定了,每一列内容不能变
if(k==0){
//订单号
order.setOrdernumber(row.getCell(k).getStringCellValue());
}
else if(k==1){
//产线
order.setLinename(row.getCell(k).getStringCellValue());
}
else if(k==2){
// 订单时间
order.setSubmittime(row.getCell(k).getDateCellValue());
}
else if(k==3){
// 交货时间
order.setDelivertime(row.getCell(k).getDateCellValue());
}
else if(k==4){
// 规格
order.setGuige(row.getCell(k).getStringCellValue());
}
else if(k==5){
// 备注
order.setSpecification(row.getCell(k).getStringCellValue());
}
else ;
}
// 将order对象添加到list中
list.add(order);
// System.out.println(order);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
}
至此,excel的解析就完成了,并且我们也得到了一个待存放入数据库的list。下一步就是常规的入库了。
写入数据库
这一步就常规了,就一个普通的insert操作,不详细写了。
@PostMapping("/import")
public RespBean importExcel(MultipartFile file){
List<Order> orders = ExcelUtils.excelToOrder(file);
// 遍历list,导入数据库。
for (Order order : orders) {
orderService.save(order);
}
return RespBean.ok("导入成功");
}
用的Mybatis。mapper、service什么的根据自己的情况写了就OK了。
写在后面
上述的excel解析方法,较为简单,要求导入的excel格式要严格规范。
要想通用性强一点的话,可以考虑以下思路:判断表头;判断单元格值的属性再进行枚举。
后续可能的问题:为了防止重复导入,在解析结果入库时,应该先判断数据库内是否已经有对应的数据了。可以根据某个唯一的字段进行查询(比如:身份证号、用户唯一id、订单号等等),有的话就不插入,没有的话则插入。
正在学习,欢迎交流,多多指教!