從零開始實現放置遊戲(六)——實現掛機戰鬥(4)匯入Excel數值配置

丶謙信發表於2019-06-21

  前面我們已經實現了在後臺管理系統中,對配置資料的增刪查改。但每次新增只能新增一條資料,實際生產中,大量資料通過手工一條一條新增不太現實。本章我們就實現通過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

  下一章,預計實現新增、修改時的引數校驗。

  歡迎催更!

相關文章