springboot+thymeleaf+layui的實(shí)現(xiàn)示例
一、背景
公司運(yùn)營(yíng)的同事有個(gè)任務(wù),提供一個(gè)數(shù)據(jù)文件給我,然后從數(shù)據(jù)庫(kù)中找出對(duì)應(yīng)的加密串再導(dǎo)出來(lái)給他。這個(gè)活不算是很難,但時(shí)不時(shí)就會(huì)有需求。
同事給我的文件有時(shí)是給excel表格,每一行有4列,逗號(hào)隔開(kāi),合并成一列數(shù)據(jù),這類文件需要把所有數(shù)據(jù)復(fù)制到文本編輯器進(jìn)行處理,把逗號(hào)替換成空格,再使用列塊編輯模式復(fù)制2、3、4列替換原來(lái)的excel數(shù)據(jù)。有時(shí)是給.DAT的文件,這類文件需要手動(dòng)修改后綴為csv,修改后就跟普通的excel表格一樣打開(kāi),去掉第一列。最后添加一行表頭,再對(duì)第一列進(jìn)行篩選去重。
去重后準(zhǔn)備導(dǎo)入到數(shù)據(jù)庫(kù)的臨時(shí)表,在此之前需要手動(dòng)清空臨時(shí)表的歷史數(shù)據(jù)。導(dǎo)入后再執(zhí)行一段sql語(yǔ)句,然后把查詢結(jié)果導(dǎo)出為excel文件給到同事。
這樣的工作重復(fù)重復(fù)再重復(fù),確實(shí)挺無(wú)趣的,何不鼓搗一個(gè)工具給同事自己去處理?
二、步驟
2.1 項(xiàng)目搭建
項(xiàng)目結(jié)構(gòu)如下圖:
創(chuàng)建項(xiàng)目,使用springboot 2.5.14
、poi 4.1.2
、mybatis
,前端使用 thymeleaf
+ layui-v2.6.8
。
具體看maven
配置
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.14</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.xxx</groupId> <artifactId>test</artifactId> <version>1.0</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- Spring框架基本的核心工具 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- SpringBoot Web容器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> </exclusion> <exclusion> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> </exclusion> </exclusions> </dependency> <!-- thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency> <!-- 阿里數(shù)據(jù)庫(kù)連接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> <!-- Mysql驅(qū)動(dòng)包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--常用工具類 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!-- io常用工具類 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <!-- excel工具 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> <exclusions> <exclusion> <groupId>org.apache.commons</groupId> <artifactId>commons-math3</artifactId> </exclusion> <exclusion> <groupId>org.zaxxer</groupId> <artifactId>SparseBitSet</artifactId> </exclusion> </exclusions> </dependency> <!-- servlet包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.7.3</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <mainClass>com.xxx.AdminApplication</mainClass> </configuration> </plugin> </plugins> </build> </project>
為了節(jié)省jar包體積,盡可能把不需要的依賴給排除。
2.2 后端處理邏輯
Controller
內(nèi)容
import com.xxx.domain.Result; import com.xxx.domain.CellItem; import com.xxx.domain.HelmetConfig; import com.xxx.service.HelmetService; import com.xxx.utils.file.DatUtil; import com.xxx.utils.poi.ExcelUtil; import org.apache.commons.io.FilenameUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.List; /** * 通用請(qǐng)求處理 * * @author admin */ @Controller public class CommonController { public static final String[] EXCEL_EXTENSION = {"xls", "xlsx", "XLS", "XLSX"}; public static final String DAT_EXTENSION = "DAT"; @Resource private HelmetService helmetService; @GetMapping(value = {"/", "/index"}) public String index(Model model) { return "index"; } /** * 通用下載請(qǐng)求 */ @GetMapping("/download") public void fileDownload(HttpServletResponse response) { List<HelmetConfig> list = helmetService.queryAll(); ExcelUtil<HelmetConfig> util = new ExcelUtil<>(HelmetConfig.class); util.exportExcel(response, list, "Sheet1"); } /** * 通用上傳請(qǐng)求(單個(gè)) */ @PostMapping("/upload") @ResponseBody public Result uploadFile(MultipartFile file) { if (file == null || file.isEmpty()) { return Result.error("文件不能為空"); } String extension = FilenameUtils.getExtension(file.getOriginalFilename()); List<CellItem> list; if (Arrays.asList(EXCEL_EXTENSION).contains(extension)) { list = ExcelUtil.getData(file); } else if (DAT_EXTENSION.equalsIgnoreCase(extension)) { list = DatUtil.readDat(file); } else { return Result.error("文件格式不正確"); } if (list.isEmpty()) { return Result.error("操作失敗,請(qǐng)重試"); } helmetService.batchAdd(list); return Result.success("操作成功,請(qǐng)點(diǎn)擊【下載文件】"); } }
數(shù)據(jù)庫(kù)根據(jù)最后的查詢sql創(chuàng)建一個(gè)視圖(View),通過(guò)mybatis對(duì)這個(gè)試圖進(jìn)行查詢,然后把結(jié)構(gòu)進(jìn)行導(dǎo)出即可。
ExcelUtil.getData()
內(nèi)容
public static List<CellItem> getData(MultipartFile file) { InputStream inputStream = null; List<CellItem> rowList = new ArrayList<>(); try { inputStream = file.getInputStream(); XSSFWorkbook wb = new XSSFWorkbook(inputStream); int ignoreRows = 0; int sheetNum = wb.getNumberOfSheets(); //for循環(huán):取前N個(gè)表,下標(biāo)從0開(kāi)始 for (int i = 0; i < sheetNum; i++) { XSSFSheet sheetI = wb.getSheetAt(i); //列數(shù) int cellSize = sheetI.getRow(0).getLastCellNum(); //第N+1行開(kāi)始,可以通過(guò)傳參,從第N+1行開(kāi)始取 for (int rowIndex = ignoreRows; rowIndex <= sheetI.getLastRowNum(); rowIndex++) { XSSFRow row = sheetI.getRow(rowIndex); if (row == null) { continue; } if (cellSize == 1) { XSSFCell cell = row.getCell(0); String cellValue = cell.getStringCellValue(); if (cellValue.contains(",")) { CellItem item = new CellItem(); String[] cells = cellValue.split(","); String deviceId = cells[1]; Boolean exists = checkExists(rowList, deviceId); if (exists) { continue; } item.setDeviceId(deviceId.trim()); item.setProductId(cells[2]); item.setMac(cells[3]); rowList.add(item); } } else if (cellSize == 4){ //在每行中的每一列,從下標(biāo)1開(kāi)始,忽略第一列,一直取到所有 CellItem item = new CellItem(); String deviceId = row.getCell(1).getStringCellValue(); Boolean exists = checkExists(rowList, deviceId); if (exists) { continue; } item.setDeviceId(deviceId.trim()); item.setProductId(row.getCell(2).getStringCellValue()); item.setMac(row.getCell(3).getStringCellValue()); rowList.add(item); } } } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (Exception e) { log.error("文件流關(guān)閉失敗:{}", e.getMessage()); } } } return rowList; } private static Boolean checkExists(List<CellItem> rowList, String key) { for (int i = 0; i < rowList.size(); i++) { CellItem item = rowList.get(i); if (item.getDeviceId().equals(key.trim())) { return Boolean.TRUE; } } return Boolean.FALSE; }
DatUtil.readDat()
public static List<CellItem> readDat(MultipartFile file) { List<CellItem> list = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { String[] split = line.split(","); String deviceId = split[1]; Boolean exists = checkExists(list, deviceId); if (exists) { continue; } CellItem item = new CellItem(); item.setDeviceId(deviceId.trim()); item.setMac(split[2]); item.setProductId(split[3]); list.add(item); } } catch (IOException e) { e.printStackTrace(); } return list; } private static Boolean checkExists(List<CellItem> rowList, String key) { for (int i = 0; i < rowList.size(); i++) { CellItem item = rowList.get(i); if (item.getDeviceId().equals(key.trim())) { return Boolean.TRUE; } } return Boolean.FALSE; }
導(dǎo)出的代碼這里省略了。
2.3 配置
application.yml
# 開(kāi)發(fā)環(huán)境配置 server: # 服務(wù)器的HTTP端口 port: 8080 servlet: # 應(yīng)用的訪問(wèn)路徑 context-path: / # Spring配置 spring: profiles: active: druid #thymeleaf 頁(yè)面的緩存開(kāi)關(guān) thymeleaf: enabled: true cache: true mode: HTML5 encoding: utf-8 suffix: .html # 文件上傳 servlet: multipart: # 單個(gè)文件大小 max-file-size: 10MB # 設(shè)置總上傳的文件大小 max-request-size: 50MB # MyBatis配置 mybatis: # 搜索指定包別名 typeAliasesPackage: com.xxx.domain # 配置mapper的掃描,找到所有的mapper.xml映射文件 mapperLocations: classpath:mapper/*.xml # 加載全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # 日志配置 logging: level: com.xxx: info org.springframework: warn
數(shù)據(jù)庫(kù)配置application-druid.yml
# 數(shù)據(jù)源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://172.16.1.2:3306/test?useUnicode=true&useSSL=false&allowLoadLocalInfile=false&autoReconnect=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8 username: root password: root #Spring Boot 默認(rèn)是不注入這些屬性值的,需要自己綁定 #druid 數(shù)據(jù)源專有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true
2.3 前端處理邏輯
把layui
的相關(guān)文件放到resources/static
目錄,再新建一個(gè)index.html
文件放入resources/templates
目錄,這兩個(gè)目錄是thymeleaf默認(rèn)的,如果要修改可以在application.yml
進(jìn)行配置。靜態(tài)文件如下:
為了壓縮jar包的體積,把所有不必要的文件都精簡(jiǎn)掉了。
以下是index.html
內(nèi)容
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>測(cè)試</title> <script th:src="@{/layui.js}"></script> <link rel="stylesheet" th:href="@{/css/layui.css}" rel="external nofollow" media="all"> </head> <body> <div class="layui-card"> <div class="layui-card-header">操作面板</div> <div class="layui-card-body"> <div class="layui-tab" lay-filter="window"> <ul class="layui-tab-title"> <li class="layui-this" lay-id="uploadTab">文件上傳</li> <li lay-id="downloadTab">文件下載</li> </ul> <div class="layui-tab-content"> <div class="layui-tab-item layui-show"> <form id="upload_form" class="layui-form" enctype="multipart/form-data"> <div class="layui-form-item"> <label class="layui-form-label">文件</label> <div class="layui-input-block"> <button type="button" class="layui-btn" id="upload"> <i class="layui-icon"></i>選擇文件 </button> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button id="btnSubmit" class="layui-btn" onclick="return false;">立即提交</button> </div> </div> </form> </div> <div class="layui-tab-item"> <div class="layui-form-item"> <label class="layui-form-label">文件</label> <div class="layui-input-block"> <button type="button" class="layui-btn" id="downloadBtn"> <i class="layui-icon"></i>下載文件 </button> </div> </div> </div> </div> </div> </div> </div> </body> </html> <script> layui.use(['upload', 'layer', 'element'], function () { let $ = layui.jquery , layer = layui.layer , element = layui.element , upload = layui.upload; //執(zhí)行實(shí)例 upload.render({ elem: '#upload' //綁定元素 , url: '/upload' //上傳接口 , accept: 'file' //允許上傳的文件類型,不寫(xiě)默認(rèn)是圖片 , acceptMime: ".xlsx,.xls,.DAT,.dat" //不寫(xiě)默認(rèn)驗(yàn)證圖片格式,一定要省略【exts】參數(shù) , auto: false //選擇文件后不自動(dòng)上傳 , bindAction: '#btnSubmit' //指向一個(gè)按鈕觸發(fā)上傳 , before: function (obj) { layer.load(); //上傳loading } ,done: function (res) { console.log(res) layer.closeAll('loading'); //關(guān)閉loading layer.alert(res.msg); if (res.code === 200) { element.tabChange('window', 'downloadTab'); } } , error: function (res) { console.error(res) layer.msg(res.msg); layer.closeAll('loading'); //關(guān)閉loading } }); $("#downloadBtn").on('click', function () { location.href = "/download"; }) }); </script>
編輯好測(cè)試沒(méi)問(wèn)題直接打包放到服務(wù)器上執(zhí)行就可以啦。
三、實(shí)現(xiàn)效果
3.1 文件導(dǎo)入
導(dǎo)入成功后會(huì)自動(dòng)切換到【文件下載】的tab頁(yè)
3.2 文件導(dǎo)出
到此這篇關(guān)于springboot+thymeleaf+layui的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)springboot thymeleaf layui內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java連接zookeeper的實(shí)現(xiàn)示例
ZooKeeper官方提供了Java API,可以通過(guò)Java代碼來(lái)連接zookeeper服務(wù)進(jìn)行操作,本文就來(lái)介紹一下java連接zookeeper的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11MyBatis-Plus中最簡(jiǎn)單的查詢操作教程(Lambda)
這篇文章主要給大家介紹了關(guān)于MyBatis-Plus中最簡(jiǎn)單的查詢操作的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03SpringBoot后端解決跨域問(wèn)題的3種方案分享
這篇文章主要給大家分享介紹了關(guān)于SpringBoot后端解決跨域問(wèn)題的3種方案,跨域指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本,它是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制,需要的朋友可以參考下2023-07-07Springboot的spring-boot-maven-plugin導(dǎo)入失敗的解決方案
這篇文章主要介紹了Springboot的spring-boot-maven-plugin導(dǎo)入失敗的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Spring?Boot中@Autowired注入為空的原因以及解決方法
最近在開(kāi)發(fā)中遇到了使用@Autowired注解自動(dòng)裝配時(shí)會(huì)報(bào)空指針,發(fā)現(xiàn)對(duì)象并沒(méi)有裝配進(jìn)來(lái),下面這篇文章主要給大家介紹了關(guān)于Spring?Boot中@Autowired注入為空的原因以及解決方法,需要的朋友可以參考下2024-01-01JavaWeb開(kāi)發(fā)中alias攔截器的使用方法
本文給大家介紹在JavaWeb開(kāi)發(fā)中alias攔截器的使用方法相關(guān)知識(shí),本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起看下吧2016-08-08SpringBoot?整合?Spring-Session?實(shí)現(xiàn)分布式會(huì)話項(xiàng)目實(shí)戰(zhàn)
本文主要介紹了SpringBoot?整合?Spring-Session?實(shí)現(xiàn)分布式會(huì)話項(xiàng)目實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07