前面我们已经实现了在后台管理系统中,对配置数据的增删查改。但每次添加只能添加一条数据,实际生产中,大量数据通过手工一条一条添加不太现实。本章我们就实现通过Excel导入配置数据的功能。这里我们还是以地图数据为例,其他配置项可参照此例。
涉及的功能点主要有对office文档的编程、文件上传功能。流程图大致如下:
一、添加依赖项
解析office文档推荐使用免费的开源组件POI,已经可以满足80%的功能需求。上传文件需要依赖commons-fileupload包。我们在pom中添加下列代码:
<!-- office组件 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
另外,之前我们配置的mvc视图解析器只能解析简单的视图,上传文件需要支持multipart。在spring-mvc.xml中添加如下配置:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="10485770"></property>
<property name="maxInMemorySize" value="10485760"></property>
</bean>
这里配置了上传最大限制10MB,对于excel上传来说足矣。
二、文件上传、解析、落库
在MapController中,我们添加3个方法
@ResponseBody
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Object importExcel(HttpServletRequest request) {
try {
ServletContext servletContext = request.getServletContext();
String uploadPath = servletContext.getRealPath("/upload");
File dir = new File(uploadPath);
if (!dir.exists()) {
dir.mkdir();
}
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(servletContext);
if (multipartResolver.isMultipart(request)) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
Iterator<String> iter = multiRequest.getFileNames();
while (iter.hasNext()) {
MultipartFile file = multiRequest.getFile(iter.next());
if (file.getSize() > 0) {
String fileName = file.getOriginalFilename();
String extension = fileName.substring(fileName.lastIndexOf("."));
if (!extension.toLowerCase().equals(".xls") && !extension.toLowerCase().equals(".xlsx")) {
throw new Exception("不支持的文档格式!请上传.xls或.xlsx格式的文档!");
}
String destFileName = fileName + "_" + System.currentTimeMillis() + extension;
File destFile = new File(uploadPath, destFileName);
file.transferTo(destFile);
List<WowMap> dataList = this.loadExcelData(destFile.getPath());
this.saveExcelData(dataList);
if (!destFile.delete()) {
logger.warn("临时文件删除失败:" + destFile.getAbsolutePath());
}
}
}
}
return CommonResult.success();
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
return CommonResult.fail();
}
}
protected List<WowMap> loadExcelData(String excelPath) throws Exception {
FileInputStream fileInputStream = new FileInputStream(excelPath);
XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheet("地图");
List<WowMap> wowMapList = new ArrayList<>();
// 处理当前页,循环读取每一行
String createUser = this.currentUserName();
for (int rowNum = 2; rowNum <= sheet.getLastRowNum(); rowNum++) {
XSSFRow row = (XSSFRow) sheet.getRow(rowNum);
String name = PoiUtil.getCellValue(row.getCell(2));
DataDict.Occupy occupy = DataDict.Occupy.getByDesc(PoiUtil.getCellValue(row.getCell(4)));
WowMap wowMap = new WowMap();
wowMap.setName(name);
wowMap.setOccupy(occupy.getCode());
wowMap.setDescription("");
wowMap.setCreateUser(createUser);
wowMapList.add(wowMap);
}
fileInputStream.close();
return wowMapList;
}
protected void saveExcelData(List<WowMap> dataList) {
wowMapManager.batchInsert(dataList);
}
MapController.java
其中,importExcel方法,时候对应前端点击导入按钮时的后端入口,在这个方法中,我们定义了临时文件上传路径,校验了文件名后缀,保存上传的文件到服务器,并在操作结束后将临时文件删除; loadExcelData方法,利用POI组件读取解析Excel数据,Excel数据怎么配我们可以自由定义,这里读取时自由调整对应的行列即可,本例使用的Excel在文末给出的源码中可以找到; saveExcelData方法,将解析到的数据列表存入数据库,这里调用的batchInsert批量添加方法,在前面讲增删查改的时候已经提前实现了。
另外,在使用POI组件读取Excel数据时,需要先判断单元格格式,我们创建一个工具类PoiUtil来实现此功能,这种在以后的其他项目中也可以使用的工具类,我们把它提取出来,放到util模块中,作为我们的通用工具包,以便日后使用。在util模块新建包com.idlewow.util.poi,并添加PoiUtil类:
package com.idlewow.util.poi;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PoiUtil {
public static String getCellValue(Cell cell) {
CellType cellType = cell.getCellType();
if (cellType.equals(CellType.STRING)) {
return cell.getStringCellValue();
} else if (cellType.equals(CellType.NUMERIC)) {
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
return date == null ? "" : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
} else {
return new DecimalFormat("0.##").format(cell.getNumericCellValue());
}
} else if (cellType.equals(CellType.FORMULA)) {
if (StringUtils.isNotBlank(cell.getStringCellValue())) {
return cell.getStringCellValue();
} else {
return cell.getNumericCellValue() + "";
}
} else if (cellType.equals(CellType.BOOLEAN)) {
return cell.getBooleanCellValue() ? "TRUE" : "FALSE";
} else {
return "";
}
}
}
PoiUtil.java
工具类提取到util模块后,需要在util模块也添加对Poi的依赖,并在rms模块添加对util的依赖。这里util模块中,依赖项的scope为provided即可,仅在编译阶段使用,因为在引用此工具包的模块中肯定已经引入了POI依赖,无需重复打包:
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
三、修改前端页面
在地图列表页面list.jsp中,添加导入excel的按钮。
<form>
…………
…………
<div class="layui-inline layui-show-xs-block">
<button type="button" class="layui-btn" onclick="xadmin.open('添加地图','add',500,500)">
<i class="layui-icon"></i>添加地图
</button>
</div>
<div class="layui-upload layui-inline layui-show-xs-block">
<button type="button" class="layui-btn layui-btn-normal" id="btnSelectFile">选择Excel</button>
<button type="button" class="layui-btn" id="btnImport">开始导入</button>
</div>
</form>
在列表页面的list.js中,绑定相应的按钮事件。
layui.use(['upload', 'table', 'form'], function () {
…………
…………
layui.upload.render({
elem: '#btnSelectFile',
url: '/manage/map/importExcel',
accept: 'file',
exts: 'xls|xlsx',
auto: false,
bindAction: '#btnImport',
done: function (result) {
if (result.code === 1) {
layer.alert(result.message, {icon: 6},
function () {
layui.layer.closeAll();
layui.table.reload('datatable');
});
} else {
layer.alert(result.message, {icon: 5});
}
}
});
});
四、运行效果
以上,excel导入的功能就全部完成了,我们运行下看下效果:
小结
本章通过导入Excel文件,实现了批量录入的功能。
源码下载地址:https://idlestudio.ctfile.com/fs/14960372-383760599
下一章,预计实现添加、修改时的参数校验。
项目交流群:329989095