• 从零开始实现放置游戏(六)——实现后台管理系统(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

  • 相关阅读:
    C++ XML解析之TinyXML篇[转]
    TinyXML:一个优秀的C++ XML解析器[转]
    nginx 出现413 Request Entity Too Large问题的解决方法
    redis配置认证密码
    《Discuz安装时候出现乱码 -- 问题解决方法》
    MySQL创建用户与授权
    CentOS 7 安装mysql
    setfacl命令 来自: http://man.linuxde.net/setfacl
    install pip3 for python 3.x
    自己制作ssl证书:自己签发免费ssl证书,为nginx生成自签名ssl证书
  • 原文地址:https://www.cnblogs.com/lyosaki88/p/idlewow_6.html
Copyright © 2020-2023  润新知