从零开始实现放置游戏(六)——实现挂机战斗(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/11061813.html

时间: 2024-10-10 07:58:34

从零开始实现放置游戏(六)——实现挂机战斗(4)导入Excel数值配置的相关文章

从零开始实现放置游戏(十二)——实现战斗挂机(3)数据字典和缓存改造

上一章,我们添加了游戏的主界面和注册登录功能.由于距离上上篇间隔较长,可能有些内容想些的后来就忘了.同时,逻辑也不复杂,所以描述比较粗略. 现在随着模块的增加,整个架构也暴露出一些问题.本章我们将对整个系统进行大规模重构. 比如,之前为了快速开发,rms模块,我们采用了直接访问数据库的方式,对于rms模块本身来说,没有什么问题. 但是,在game模块中,对于频繁访问的.不经常改变的数据或接口,希望采用缓存的方式,将数据缓存起来,减少后端压力,同时加快响应速度,从而提升体验. 之前rms模块中尝试

从零开始实现放置游戏(八)——实现挂机战斗(6)代码重构

前几张,我们主要实现了升级经验.人物等级属性.地图.地图怪物,这四种配置的增删查改以及Excel导入功能.我们主要以地图怪物为例,因此在文章末尾提供的源代码中只实现了地图怪物这部分的逻辑功能. 如果你照猫画虎,把4种配置功能的逻辑全部实现的话,就会发现,增删查改的代码基本相同,除了SQL语句和模型对象不同,其他地方变化不大. 本章我们利用泛型模板,对整个系统就行重构.在重构结束后,你就会发现写代码简直就是TMD艺术! 后端重构 idlewow-core 我们从最底层开始,首先重构位于core模块

从零开始实现放置游戏(九)——实现后台管理系统(7)地图选择控件

前面做了地图怪物的添加,删除,查询等功能.但添加怪物的时候,需要选择怪物所在地图.前几张的源代码中,我忘了把这部分改回去,所以如果想要成功添加,需要自己改一下html界面,手动填写怪物所在地图的ID.然而,我们配置的时候,地图ID并不是固定的,而是数据库自增的.所以这里最好做成一个弹窗,点击后弹出一个地图列表,让我们手动选择怪物所在地图. 本章我们就实现这样一个弹窗控件,实现对地图的选择.后面如果有选择怪物,选择装备等需求,都可照猫画虎.整个过程的流程大致如下: 实现步骤 首先,我们给弹出的地图

第六章 Linux的文件权限与目录配置

第六章 Linux的文件权限与目录配置 6.1Linux用户身份与用户组记录的文件 每个账号都可以有多个用户组支持 /etc/passwd              用户账号 /etc/shadow             账号密码 /etc/group                 组账号 6.2权限 文件的RWX权限只涉及对文件内容的约束 文件夹的RWX权限只涉及对文件夹下的文件的约束 ls原理:根据身份对文件夹访问时,若该身份对该文件夹为r--,则结果只返回文件名列表:若为r-x,返回文

Unity3D游戏开发从零单排(五) - 导入CS模型到Unity3D

游戏动画基础 Animation组件 Animation组件是对于老的动画系统来说的. 老的动画形同对应的动画就是clip,每个运动都是一段单独的动画,使用Play()或CrossFade(),直接播放动画 或淡入淡出播放动画. animation.Play("name"); animation.CrossFade("name"); 下面的是它的几个属性 Animation:默认的动画片段: Aniamtions:包含的动画片段: Play Automaticall

鸟哥的Linux私房菜_基础版_学习笔记2:第六章 linux的文件权限与目录配置

第六章 linux的文件权限与目录配置 6.1.用户与用户组 1.文件拥有者 2.群组概念 3.其他人的概念 在Linux里面,任何一个文件都有"User,Group和Others"3种身份的个别权限. 我们以王三毛为例,王三毛这个『文件』的拥有者为王三毛,他属于王大毛这个群组, 而张小猪相对于王三毛,则只是一个『其他人(others)』而已. 不过,这里有个特殊的人物要来介绍的,那就是『万能的天神』!这个天神具有无限的神力, 所以他可以到达任何他想要去的地方,呵呵!那个人在Linux

C#程序员的春天之从零开始学习unity3D游戏开发入门教程一(前言)

我们不在“拖控件”,我们也有春天 序言:之前断断续续谢了一点unity的教程,但是不全面.这里表示歉意.今天准备重新做一套.今天也是换了新工作,也是新的开始.也希望以此来激励自己把这个教程一直写下去.想要学习unity的同学可以加这个群(326535328),可能会在qq课堂在线讲解一些相关知识. 乱扯一下:为什么说这时C#程序员的春天呢?在大多数java或者其他程序员的眼里,我们.net平台的程序员同胞都背负着”拖控件“的这么一个标签.某种程度上讲,.net的快速入门的精髓就是拖控件.然而外人

从零开始的linux 第六章

宇宙浩瀚,无边无界,地球也不过是太阳系八大行星之一,作为无数行星中的一枚,地球实在算不上特别....但...小编的博客,绝~对~特~别~~ (↑看过小编博客的同学回答) 好啦~~这周小编就不废话了,直接进入正题吧~~ 上周小编讲了设置和取消别名 alias ualias ,使别名永久生效的配置文件 /etc/bashrc 以及 ~/.bashrc 查看当前所在的路径 pwd ,文本编译器 nano.小编这里就不做过多的演示啦,已经忘记的同学可以回顾一下第五章的内容~~ 小编接下来就直接开始说这周

Java从零开始学三十六(JAVA IO- 字符流)

一.字符流 BufferedReader:BufferedReader是从缓冲区之中读取内容,所有的输入的字节数据都将放在缓冲区之中 BufferedWriter:把一批数据写入到缓冲区,当缓冲区区的满时,再把缓冲区的内容写到字符输出流中 二.对文本文件的读写 2.1.字符输入流 2.2.字符输出流 2.3.综合使用 package com.pb.io.buffered; import java.io.BufferedReader; import java.io.BufferedWriter;