从零开始实现放置游戏(六)——实现后台管理系统(4)Excel批量导入
前面我们已经实现了在后台管理系统中,对配置数据的增删查改。但每次添加只能添加一条数据,实际生产中,大量数据通过手工一条一条添加不太现实。本章我们就实现通过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
本文原文地址:https://www.cnblogs.com/lyosaki88/p/idlewow_6.html
下一章,预计实现添加、修改时的参数校验。
项目交流群:329989095
从零开始实现放置游戏(六)——实现后台管理系统(4)Excel批量导入的更多相关文章
- 从零开始实现放置游戏(七)——实现挂机战斗(5)RMS系统后台参数校验
前面几章实现了在RMS系统中进行数据的增删查改以及通过Excel批量导入.但仍有遗留的问题,比如在新增或编辑时,怪物的生命值.护甲等数据我们可以输入负值,这种数据是不合理且没有意义的.本章我们就实现服 ...
- 从零开始实现放置游戏(六)——实现挂机战斗(4)导入Excel数值配置
前面我们已经实现了在后台管理系统中,对配置数据的增删查改.但每次添加只能添加一条数据,实际生产中,大量数据通过手工一条一条添加不太现实.本章我们就实现通过Excel导入配置数据的功能.这里我们还是以地 ...
- 从零开始实现放置游戏(十)——实现战斗挂机(1)hessian服务端搭建
前面实现RMS系统时,我们让其直接访问底层数据库.后面我们在idlewow-game模块实现游戏逻辑时,将不再直接访问底层数据,而是通过hessian服务暴露接口给表现层. 本章,我们先把hessia ...
- 从零开始实现放置游戏(十三)——实现战斗挂机(4)添加websocket组件
前两张,我们已经实现了登陆界面和游戏的主界面.不过游戏主界面的数据都是在前端写死的文本,本章我们给game模块添加websocket组件,实现前后端通信,这样,前端的数据就可以从后端动态获取到了. 一 ...
- 从零开始编写自己的C#框架(8)——后台管理系统功能设计
还是老规矩先吐下槽,在规范的开发过程中,这个时候应该是编写总体设计(概要设计)的时候,不过对于中小型项目来说,过于规范的遵守软件工程,编写太多文档也会拉长进度,一般会将它与详细设计合并到一起来处理,所 ...
- vue从入门到女装??:从零开始搭建后台管理系统(二)用vue-docute生成线上文档
教程 vue从入门到女装??:从零开始搭建后台管理系统(一)安装框架 一个系统开发完成了总要有操作说明手册,接口文档之类的东西吧?这种要全部纯手写就很麻烦了,可以借助一些插件,比如: vue-docu ...
- 从零开始搭建vue+element-ui后台管理系统项目到上线
前言 之前有些过移动端的项目搭建的文章,感觉不写个pc端管理系统老感觉少了点什么,最近公司项目比较多,恰巧要做一个申报系统的后台管理系统,鉴于对vue技术栈比较熟悉,所以考虑还是使用vue技术栈来做: ...
- 从零开始搞后台管理系统(1)——shin-admin
shin 的读音是[ʃɪn],谐音就是行,寓意可行的后台管理系统,shin-admin 的特点是: 站在巨人的肩膀上,依托Umi 2.Dva 2.Ant Design 3和React 16.8搭建 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(持续更新中...)
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 任务调度系统界面 http: ...
随机推荐
- WPF中DataGrid在没有数据的时候也可以显示水平滚动条
今天做项目中遇到个问题,就是页面加载后默认DataGrid是不加载数据的,但是DataGrid的列很多,就导致了运行效果上,此窗口的DataGrid没有水平滚动条,类似图片的效果. 经过百度和摸索,使 ...
- Web前端基础(8):JavaScript(二)
1. 数据类型转换 1.1 将数值类型转换成字符串类型 1.1.1 隐式转换 在js中,当运算符在运算时,如果两边数据不统一,CPU就无法计算,这时我们编译器会自动将运算符两边的数据做一个数据类型转换 ...
- MySQL学习——查询表里的数据
MySQL学习——查询表里的数据 摘要:本文主要学习了使用DQL语句查询表里数据的方法. 数据查询 语法 select [distinct] 列1 [as '别名1'], ..., 列n [as '别 ...
- CSS3/CSS之居中解析(水平+垂直居中、水平居中,垂直居中)
首先,我们来看下垂直居中: (1).如果是单行文本,则可以设置的line-height的数值,让其等于父级元素的高度! <!DOCTYPE html> <html lang=&quo ...
- SAP MM 采购信息记录中价格单位转换因子的修改
SAP MM 采购信息记录中价格单位转换因子的修改 如下采购信息记录, Order unit 跟 base UoM 一致,都是 PC. 采购价格是8849.56 CNY / 10000W, 由于业务人 ...
- Linux下 vim代码编译器的使用
首先在命令行模式下输入代码: vim ok.c 创建c文件 如果发现vim没有被安装的话,输入以下代码对vim进行安装: sudo apt install vim 安装完毕之后再输入第一行代码,之后进 ...
- ABP进阶教程0 - 目录
ABP进阶教程 本教程主要讲解如何基于ABP实现条件查询/分页/排序/导出/打印等功能. 源码已分享: GitHub Gitee ABP进阶教程0 - 目录 ABP进阶教程1 - 条件查询 A ...
- Saltstack_使用指南11_配置管理-状态之间依赖关系
1. 说明 下文的案例是根据上一篇文章进行的修改.因此请优先读取上一篇文章内容<Saltstack_10_配置管理-状态模块> 并且目录进行了变化,从 /srv/salt/lamp 变为了 ...
- Mysql—配置文件my.ini或my.cnf的详解
[mysqld] log_bin = mysql-bin binlog_format = mixed expire_logs_days = # 超过7天的binlog删除 slow_query_log ...
- 图Lasso求逆协方差矩阵(Graphical Lasso for inverse covariance matrix)
图Lasso求逆协方差矩阵(Graphical Lasso for inverse covariance matrix) 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/ka ...