java進(jìn)階解析Springboot上傳excel存入數(shù)據(jù)庫(kù)步驟
一、導(dǎo)入依賴
這里還是用了Apache的POI插件,現(xiàn)在一般的springboot解析excel基本都用它 。
<!-- 文件上傳,解析文件需要的依賴--> <!--poi對(duì)excel2007以上版本的支持--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.0.0</version> </dependency> <!-- poi對(duì)excel2003以下版本的支持 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.0.0</version> </dependency>
二、前端實(shí)現(xiàn)
<button type="button" class="layui-btn layui-btn-normal layui-btn-radius" id="bookFileUpload"> <i class="layui-icon"></i>文件導(dǎo)入 </button>
/** * 文件上傳--批量導(dǎo)入 */ layui.use('upload', function () { var upload = layui.upload; var layer = layui.layer; // layer.msg('上傳中', {icon: 16, time: 0,shade: 0.3}); //執(zhí)行實(shí)例 var uploadInst = upload.render({ elem: '#bookFileUpload', //綁定元素 url: '/library/book/batchAddBooks/', //上傳接口 exts: 'xlsx|xls', //限制文件類型 done: function (res) { //上傳完畢回調(diào) if (res.code == '1') { successMsg("導(dǎo)入成功"); } else { errorMsg("導(dǎo)入失敗" + res); } }, error: function (res) { //請(qǐng)求異?;卣{(diào) errorMsg("系統(tǒng)異常" + res); } }); });
說(shuō)明一下,這里還是用了layui的框架去實(shí)現(xiàn)的,layui是我用過(guò)的前端框架里邊坑相對(duì)較少的一個(gè)不錯(cuò)的簡(jiǎn)單并且很容易入門(mén)的框架。
三、后臺(tái)邏輯
1.首先寫(xiě)一個(gè)用于導(dǎo)入數(shù)據(jù)的model實(shí)體類,對(duì)應(yīng)數(shù)據(jù)庫(kù)的一張表。
/** * 書(shū)籍實(shí)體 */ @Data public class Book { //主鍵id private int bid; //書(shū)名 private String bookname; //作者 private String author; //書(shū)的類型 武俠、言情等 private String booktype; //出版社 private String publisher; //出版時(shí)間 private String publicationdate; //發(fā)行價(jià)格 private int price; //發(fā)行狀態(tài) private String bookstate; //備注 private String comment; }
這里使用了lombok的插件工具,所以可以省去寫(xiě)setget方法,只需要@Data即可。
lombok的依賴,可用可不用,看你自己:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
2.接著寫(xiě)一下前端上傳過(guò)來(lái)excel,后臺(tái)的解析處理邏輯。
下面是重點(diǎn):
package com.example.library.utils.fileUtiles; import com.example.library.model.Book; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; /** * @Author Summer_DM * @Summary TODO 操作Excel的工具類 * @Version 1.0 * @Date 2021/9/16 下午 03:44 **/ public class ExcelUtils { private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class); //Map<String,Object> map = new HashMap<String, Object>(); private static final String XLS = "xls"; private static final String XLSX = "xlsx"; private final static String DATE_FORMAT = "yyyy-MM-dd"; /** * 根據(jù)文件后綴名類型獲取對(duì)應(yīng)的工作簿對(duì)象 * @param inputStream 讀取文件的輸入流 * @param fileType 文件后綴名類型(xls或xlsx) * @return 包含文件數(shù)據(jù)的工作簿對(duì)象 * @throws IOException */ public static Workbook getWorkbook(InputStream inputStream, String fileType) throws Exception { Workbook workbook = null; //根據(jù)文件后綴名不同(xls和xlsx)獲得不同的Workbook實(shí)現(xiàn)類對(duì)象 if (fileType.equalsIgnoreCase(XLS)) { //2003 workbook = new HSSFWorkbook(inputStream); } else if (fileType.equalsIgnoreCase(XLSX)) { //2007及以上 workbook = new XSSFWorkbook(inputStream); }else { throw new Exception("請(qǐng)上傳excel文件!"); } return workbook; } /** * 讀取Excel文件內(nèi)容 * @param fileName 要讀取的Excel文件所在路徑 * @return 讀取結(jié)果列表,讀取失敗時(shí)返回null */ public static List<Book> readExcel(String fileName) { Workbook workbook = null; FileInputStream inputStream = null; try { // 獲取Excel后綴名 String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()); // 獲取Excel文件 File excelFile = new File(fileName); if (!excelFile.exists()) { logger.warn("指定的Excel文件不存在!"); } // 獲取Excel工作簿 inputStream = new FileInputStream(excelFile); workbook = getWorkbook(inputStream, fileType); // 讀取excel中的數(shù)據(jù) List<Book> resultDataList = parseExcel(workbook); return resultDataList; } catch (Exception e) { logger.warn("解析Excel失敗,文件名:" + fileName + " 錯(cuò)誤信息:" + e.getMessage()); return null; } finally { try { if (null != workbook) { workbook.close(); } if (null != inputStream) { inputStream.close(); } } catch (Exception e) { logger.warn("關(guān)閉數(shù)據(jù)流出錯(cuò)!錯(cuò)誤信息:" + e.getMessage()); return null; } } } /** * 讀取Excel文件內(nèi)容 * @param file 上傳的Excel文件 * @return 讀取結(jié)果列表,讀取失敗時(shí)返回null */ public static List<Book> readExcel(MultipartFile file) { Workbook workbook = null; try { //判斷文件是否存在 if(null == file){ logger.warn("解析Excel失敗,文件不存在!"); return null; } // 獲取Excel后綴名 String fileName = file.getOriginalFilename(); if (fileName == null || fileName.isEmpty() || fileName.lastIndexOf(".") < 0) { logger.warn("解析Excel失敗,因?yàn)楂@取到的Excel文件名非法!"); return null; } String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()); // 獲取Excel工作簿 workbook = getWorkbook(file.getInputStream(), fileType); // 讀取excel中的數(shù)據(jù) List<Book> resultDataList = parseExcel(workbook); return resultDataList; } catch (Exception e) { logger.warn("解析Excel失敗,文件名:" + file.getOriginalFilename() + " 錯(cuò)誤信息:" + e.getMessage()); return null; } finally { try { if (null != workbook) { workbook.close(); } } catch (Exception e) { logger.warn("關(guān)閉數(shù)據(jù)流出錯(cuò)!錯(cuò)誤信息:" + e.getMessage()); return null; } } } /** * 解析Excel數(shù)據(jù) * @param workbook Excel工作簿對(duì)象 * @return 解析結(jié)果 */ private static List<Book> parseExcel(Workbook workbook) { List<Book> resultDataList = new ArrayList<>(); //獲取所有的工作表的的數(shù)量 int numOfSheet = workbook.getNumberOfSheets(); System.out.println(numOfSheet+"--->numOfSheet"); // 解析sheet,此處會(huì)讀取所有腳頁(yè)的行數(shù)據(jù),若只想讀取指定頁(yè),不要for循環(huán),直接給sheetNum賦值, // 腳頁(yè)從0開(kāi)始(通常情況Excel都只有一頁(yè),所以此處未進(jìn)行進(jìn)一步處理) for (int sheetNum = 0; sheetNum < numOfSheet; sheetNum++) { //獲取一個(gè)sheet也就是一個(gè)工作本。 Sheet sheet = workbook.getSheetAt(sheetNum); // 校驗(yàn)sheet是否合法 if (sheet == null) { continue; } //獲取一個(gè)sheet有多少Row //int lastRowNum = sheet.getLastRowNum(); //if(lastRowNum == 0) { // continue; //} // 獲取第一行數(shù)據(jù) int firstRowNum = sheet.getFirstRowNum(); Row firstRow = sheet.getRow(firstRowNum); if (null == firstRow) { logger.warn("解析Excel失敗,在第一行沒(méi)有讀取到任何數(shù)據(jù)!"); } // 解析每一行的數(shù)據(jù),構(gòu)造數(shù)據(jù)對(duì)象 int rowStart = firstRowNum + 1; //獲取第幾行 //獲得當(dāng)前行的列數(shù) int rowEnd = sheet.getPhysicalNumberOfRows(); for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) { Row row = sheet.getRow(rowNum); if (null == row) { continue; } Book resultData = convertRowToData(row); if (null == resultData) { logger.warn("第 " + row.getRowNum() + "行數(shù)據(jù)不合法,已忽略!"); continue; } resultDataList.add(resultData); } } return resultDataList; } /** * 將單元格內(nèi)容轉(zhuǎn)換為字符串 * @param cell * @return */ private static String convertCellValueToString(Cell cell) { String returnValue = null; if(cell==null){ return returnValue; } //如果當(dāng)前單元格內(nèi)容為日期類型,需要特殊處理 String dataFormatString = cell.getCellStyle().getDataFormatString(); if(dataFormatString.equals("m/d/yy")){ returnValue = new SimpleDateFormat(DATE_FORMAT).format(cell.getDateCellValue()); return returnValue; } switch (cell.getCellType()) { case NUMERIC: //數(shù)字 Double doubleValue = cell.getNumericCellValue(); // 格式化科學(xué)計(jì)數(shù)法,取一位整數(shù) DecimalFormat df = new DecimalFormat("0"); returnValue = df.format(doubleValue); break; case STRING: //字符串 returnValue = cell.getStringCellValue(); break; case BOOLEAN: //布爾 Boolean booleanValue = cell.getBooleanCellValue(); returnValue = booleanValue.toString(); break; case BLANK: // 空值 break; case FORMULA: // 公式 returnValue = cell.getCellFormula(); break; case ERROR: // 故障 break; default: break; } return returnValue; } /** * 提取每一行中需要的數(shù)據(jù),構(gòu)造成為一個(gè)結(jié)果數(shù)據(jù)對(duì)象 *將excel的內(nèi)容賦給實(shí)體類model * 當(dāng)該行中有單元格的數(shù)據(jù)為空或不合法時(shí),忽略該行的數(shù)據(jù) * * @param row 行數(shù)據(jù) * @return 解析后的行數(shù)據(jù)對(duì)象,行數(shù)據(jù)錯(cuò)誤時(shí)返回null */ private static Book convertRowToData(Row row) { Book resultData = new Book(); Cell cell; int cellNum = 0; //獲取書(shū)籍名稱 cell = row.getCell(cellNum++); String bookName = convertCellValueToString(cell); if (null == bookName || "".equals(bookName)) { // 書(shū)籍名稱為空 resultData.setBookname(bookName); } else { resultData.setBookname(bookName); } // 獲取書(shū)籍作者 cell = row.getCell(cellNum++); String author = convertCellValueToString(cell); if (null == author || "".equals(author)) { // 書(shū)籍作者為空 resultData.setAuthor(author); } else { resultData.setAuthor(author); } // 獲取書(shū)籍類型 cell = row.getCell(cellNum++); String type = convertCellValueToString(cell); if (null == type || "".equals(type)) { // 書(shū)籍類型為空 resultData.setBooktype(type); } else { resultData.setBooktype(type); } //獲取出版單位 cell = row.getCell(cellNum++); String publisher = convertCellValueToString(cell); if (null == publisher || "".equals(publisher)) { // 出版單位為空 resultData.setPublisher(publisher); } else { resultData.setPublisher(publisher); } //獲取出版時(shí)間 cell = row.getCell(cellNum++); String publicationdate = convertCellValueToString(cell); if (null == publicationdate || "".equals(publicationdate)) { // 出版時(shí)間為空 resultData.setPublicationdate(publicationdate); } else { resultData.setPublicationdate(publicationdate); } //獲取價(jià)格 cell = row.getCell(cellNum++); String price = convertCellValueToString(cell); if (null == price || "".equals(price)) { // 價(jià)格為空 resultData.setPrice(Integer.parseInt(price)); } else { resultData.setPrice(Integer.parseInt(price)); } //獲取借閱狀態(tài) cell = row.getCell(cellNum++); String bookstate = convertCellValueToString(cell); if (null == bookstate || "".equals(bookstate)) { // 借閱狀態(tài)為空 resultData.setBookstate(bookstate); } else { resultData.setBookstate(bookstate); } //獲取備注 cell = row.getCell(cellNum++); String comment = convertCellValueToString(cell); if (null == comment || "".equals(comment)) { // 備注為空 resultData.setComment(comment); } else { resultData.setComment(comment); } return resultData; } /** * 生成Excel文件 * @param outputStream */ public void writeExcel(OutputStream outputStream){ /** * 這個(gè)outputstream可以來(lái)自與文件的輸出流, * 也可以直接輸出到response的getOutputStream()里面 * 然后用戶就可以直接解析到你生產(chǎn)的excel文件了 */ Workbook wb = new SXSSFWorkbook(100); //創(chuàng)建一個(gè)工作本 Sheet sheet = wb.createSheet("sheet"); //通過(guò)一個(gè)sheet創(chuàng)建一個(gè)Row Row row = sheet.createRow(0); for(int i =0;i<10;i++){ //通過(guò)row創(chuàng)建一個(gè)cell Cell cell = row.createCell(i); cell.setCellValue("這是第"+i+"個(gè)cell"); } try { wb.write(outputStream); wb.close(); }catch (IOException e){ e.printStackTrace(); } } }
3.最后寫(xiě)一下解析完成,存入數(shù)據(jù)庫(kù)。
<insert id="insertUsers"> insert into user(username,sex,tel,institute,profession,classname,stuid,password,type) values <foreach collection="users" index="" item="item" separator=","> ( #{item.username,jdbcType=VARCHAR}, #{item.sex,jdbcType=VARCHAR}, #{item.tel,jdbcType=VARCHAR}, #{item.institute,jdbcType=VARCHAR}, #{item.profession,jdbcType=VARCHAR}, #{item.classname,jdbcType=VARCHAR}, #{item.stuid,jdbcType=VARCHAR}, #{item.password,jdbcType=VARCHAR}, #{item.type,jdbcType=VARCHAR} ) </foreach> </insert>
三、頁(yè)面效果
點(diǎn)擊導(dǎo)入用戶,選擇excel文件即可。
四、可能會(huì)遇到的問(wèn)題
因?yàn)閟pringboot現(xiàn)在使用的poi的支持已經(jīng)達(dá)到5.0以上版本了,但是他整合起來(lái)可能會(huì)出現(xiàn)一些小異常,雖然不影響我們正常使用,但是控制臺(tái)會(huì)一直出現(xiàn),也很煩,所以這里 給一個(gè)解決辦法。
/** * @Author Summer_DM * @Summary TODO 專門(mén)用來(lái)處理SpringBoot應(yīng)用集成poi 5.0.0啟動(dòng)報(bào)錯(cuò)java.io.FileNotFoundException, * TODO 因?yàn)槭褂胮oi 5.0.0解析excel,會(huì)有一些找不到j(luò)ar包的問(wèn)題,但是去maven倉(cāng)庫(kù)查看發(fā)現(xiàn)jar包都在,所以可以不必理會(huì) * TODO java.io.FileNotFoundException: E:\software\apache\maven\maven-repository\library_repo\org\apache\xmlgraphics\batik-ttf2svg\1.13\lib\batik-i18n-1.13.jar (系統(tǒng)找不到指定的路徑。) * @Version 1.0 * @Date 2021/9/18 下午 09:29 **/ @Configuration public class MyStandardJarScanner { @Bean public TomcatServletWebServerFactory tomcatFactory() { return new TomcatServletWebServerFactory() { @Override protected void postProcessContext(Context context) { ((StandardJarScanner) context.getJarScanner()).setScanManifest(false); } }; } }
比較簡(jiǎn)單易學(xué),網(wǎng)上也有很多教程,有需要的可以一起來(lái)交流一下,哈哈?。?!
以上就是java進(jìn)階Springboot上傳excel存入數(shù)據(jù)庫(kù)步驟的詳細(xì)內(nèi)容,更多關(guān)于Springboot上傳excel的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mybatis打印的sql日志不寫(xiě)入到log文件的問(wèn)題及解決
這篇文章主要介紹了mybatis打印的sql日志不寫(xiě)入到log文件的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08Java?多線程并發(fā)編程提高數(shù)據(jù)處理效率的詳細(xì)過(guò)程
這篇文章主要介紹了Java?多線程并發(fā)編程提高數(shù)據(jù)處理效率,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04從java源碼分析線程池(池化技術(shù))的實(shí)現(xiàn)原理
這篇文章主要介紹了從java源碼分析線程池(池化技術(shù))的實(shí)現(xiàn)原理,池化技術(shù)是一種編程技巧,當(dāng)程序出現(xiàn)高并發(fā)時(shí),能夠明顯的優(yōu)化程序,降低系統(tǒng)頻繁創(chuàng)建銷(xiāo)毀連接等額外開(kāi)銷(xiāo),下文更多的相關(guān)介紹需要的小伙伴可以參考一下2022-04-04Spring Security基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)認(rèn)證過(guò)程解析
這篇文章主要介紹了Spring Security基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)認(rèn)證過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系
這篇文章主要介紹了關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系,WeakHashMap從名字可以得知主要和Map有關(guān),不過(guò)還有一個(gè)Weak,我們就更能自然而然的想到這里面還牽扯到一種弱引用結(jié)構(gòu),因此想要徹底搞懂,我們還需要知道四種引用,需要的朋友可以參考下2023-09-09詳解java CountDownLatch和CyclicBarrier在內(nèi)部實(shí)現(xiàn)和場(chǎng)景上的區(qū)別
這篇文章主要介紹了詳解java CountDownLatch和CyclicBarrier在內(nèi)部實(shí)現(xiàn)和場(chǎng)景上的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05