java進階解析Springboot上傳excel存入數(shù)據(jù)庫步驟
一、導(dǎo)入依賴
這里還是用了Apache的POI插件,現(xiàn)在一般的springboot解析excel基本都用它 。
<!-- 文件上傳,解析文件需要的依賴-->
<!--poi對excel2007以上版本的支持-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
<!-- poi對excel2003以下版本的支持 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.0.0</version>
</dependency>
二、前端實現(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í)行實例
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) {
//請求異?;卣{(diào)
errorMsg("系統(tǒng)異常" + res);
}
});
});
說明一下,這里還是用了layui的框架去實現(xiàn)的,layui是我用過的前端框架里邊坑相對較少的一個不錯的簡單并且很容易入門的框架。
三、后臺邏輯
1.首先寫一個用于導(dǎo)入數(shù)據(jù)的model實體類,對應(yīng)數(shù)據(jù)庫的一張表。
/**
* 書籍實體
*/
@Data
public class Book {
//主鍵id
private int bid;
//書名
private String bookname;
//作者
private String author;
//書的類型 武俠、言情等
private String booktype;
//出版社
private String publisher;
//出版時間
private String publicationdate;
//發(fā)行價格
private int price;
//發(fā)行狀態(tài)
private String bookstate;
//備注
private String comment;
}
這里使用了lombok的插件工具,所以可以省去寫setget方法,只需要@Data即可。
lombok的依賴,可用可不用,看你自己:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.接著寫一下前端上傳過來excel,后臺的解析處理邏輯。
下面是重點:
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ù)文件后綴名類型獲取對應(yīng)的工作簿對象
* @param inputStream 讀取文件的輸入流
* @param fileType 文件后綴名類型(xls或xlsx)
* @return 包含文件數(shù)據(jù)的工作簿對象
* @throws IOException
*/
public static Workbook getWorkbook(InputStream inputStream, String fileType) throws Exception {
Workbook workbook = null;
//根據(jù)文件后綴名不同(xls和xlsx)獲得不同的Workbook實現(xiàn)類對象
if (fileType.equalsIgnoreCase(XLS)) {
//2003
workbook = new HSSFWorkbook(inputStream);
} else if (fileType.equalsIgnoreCase(XLSX)) {
//2007及以上
workbook = new XSSFWorkbook(inputStream);
}else {
throw new Exception("請上傳excel文件!");
}
return workbook;
}
/**
* 讀取Excel文件內(nèi)容
* @param fileName 要讀取的Excel文件所在路徑
* @return 讀取結(jié)果列表,讀取失敗時返回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 + " 錯誤信息:" + 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ù)流出錯!錯誤信息:" + e.getMessage());
return null;
}
}
}
/**
* 讀取Excel文件內(nèi)容
* @param file 上傳的Excel文件
* @return 讀取結(jié)果列表,讀取失敗時返回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失敗,因為獲取到的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() + " 錯誤信息:" + e.getMessage());
return null;
} finally {
try {
if (null != workbook) {
workbook.close();
}
} catch (Exception e) {
logger.warn("關(guān)閉數(shù)據(jù)流出錯!錯誤信息:" + e.getMessage());
return null;
}
}
}
/**
* 解析Excel數(shù)據(jù)
* @param workbook Excel工作簿對象
* @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,此處會讀取所有腳頁的行數(shù)據(jù),若只想讀取指定頁,不要for循環(huán),直接給sheetNum賦值,
// 腳頁從0開始(通常情況Excel都只有一頁,所以此處未進行進一步處理)
for (int sheetNum = 0; sheetNum < numOfSheet; sheetNum++) {
//獲取一個sheet也就是一個工作本。
Sheet sheet = workbook.getSheetAt(sheetNum);
// 校驗sheet是否合法
if (sheet == null) {
continue;
}
//獲取一個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失敗,在第一行沒有讀取到任何數(shù)據(jù)!");
}
// 解析每一行的數(shù)據(jù),構(gòu)造數(shù)據(jù)對象
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é)計數(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)造成為一個結(jié)果數(shù)據(jù)對象
*將excel的內(nèi)容賦給實體類model
* 當(dāng)該行中有單元格的數(shù)據(jù)為空或不合法時,忽略該行的數(shù)據(jù)
*
* @param row 行數(shù)據(jù)
* @return 解析后的行數(shù)據(jù)對象,行數(shù)據(jù)錯誤時返回null
*/
private static Book convertRowToData(Row row) {
Book resultData = new Book();
Cell cell;
int cellNum = 0;
//獲取書籍名稱
cell = row.getCell(cellNum++);
String bookName = convertCellValueToString(cell);
if (null == bookName || "".equals(bookName)) {
// 書籍名稱為空
resultData.setBookname(bookName);
} else {
resultData.setBookname(bookName);
}
// 獲取書籍作者
cell = row.getCell(cellNum++);
String author = convertCellValueToString(cell);
if (null == author || "".equals(author)) {
// 書籍作者為空
resultData.setAuthor(author);
} else {
resultData.setAuthor(author);
}
// 獲取書籍類型
cell = row.getCell(cellNum++);
String type = convertCellValueToString(cell);
if (null == type || "".equals(type)) {
// 書籍類型為空
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);
}
//獲取出版時間
cell = row.getCell(cellNum++);
String publicationdate = convertCellValueToString(cell);
if (null == publicationdate || "".equals(publicationdate)) {
// 出版時間為空
resultData.setPublicationdate(publicationdate);
} else {
resultData.setPublicationdate(publicationdate);
}
//獲取價格
cell = row.getCell(cellNum++);
String price = convertCellValueToString(cell);
if (null == price || "".equals(price)) {
// 價格為空
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){
/**
* 這個outputstream可以來自與文件的輸出流,
* 也可以直接輸出到response的getOutputStream()里面
* 然后用戶就可以直接解析到你生產(chǎn)的excel文件了
*/
Workbook wb = new SXSSFWorkbook(100);
//創(chuàng)建一個工作本
Sheet sheet = wb.createSheet("sheet");
//通過一個sheet創(chuàng)建一個Row
Row row = sheet.createRow(0);
for(int i =0;i<10;i++){
//通過row創(chuàng)建一個cell
Cell cell = row.createCell(i);
cell.setCellValue("這是第"+i+"個cell");
}
try {
wb.write(outputStream);
wb.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
3.最后寫一下解析完成,存入數(shù)據(jù)庫。
<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>
三、頁面效果
點擊導(dǎo)入用戶,選擇excel文件即可。

四、可能會遇到的問題
因為springboot現(xiàn)在使用的poi的支持已經(jīng)達到5.0以上版本了,但是他整合起來可能會出現(xiàn)一些小異常,雖然不影響我們正常使用,但是控制臺會一直出現(xiàn),也很煩,所以這里 給一個解決辦法。
/**
* @Author Summer_DM
* @Summary TODO 專門用來處理SpringBoot應(yīng)用集成poi 5.0.0啟動報錯java.io.FileNotFoundException,
* TODO 因為使用poi 5.0.0解析excel,會有一些找不到j(luò)ar包的問題,但是去maven倉庫查看發(fā)現(xiàn)jar包都在,所以可以不必理會
* 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);
}
};
}
}
比較簡單易學(xué),網(wǎng)上也有很多教程,有需要的可以一起來交流一下,哈哈?。?!
以上就是java進階Springboot上傳excel存入數(shù)據(jù)庫步驟的詳細內(nèi)容,更多關(guān)于Springboot上傳excel的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mybatis打印的sql日志不寫入到log文件的問題及解決
這篇文章主要介紹了mybatis打印的sql日志不寫入到log文件的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Java?多線程并發(fā)編程提高數(shù)據(jù)處理效率的詳細過程
這篇文章主要介紹了Java?多線程并發(fā)編程提高數(shù)據(jù)處理效率,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04
從java源碼分析線程池(池化技術(shù))的實現(xiàn)原理
這篇文章主要介紹了從java源碼分析線程池(池化技術(shù))的實現(xiàn)原理,池化技術(shù)是一種編程技巧,當(dāng)程序出現(xiàn)高并發(fā)時,能夠明顯的優(yōu)化程序,降低系統(tǒng)頻繁創(chuàng)建銷毀連接等額外開銷,下文更多的相關(guān)介紹需要的小伙伴可以參考一下2022-04-04
Spring Security基于數(shù)據(jù)庫實現(xiàn)認(rèn)證過程解析
這篇文章主要介紹了Spring Security基于數(shù)據(jù)庫實現(xiàn)認(rèn)證過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系
這篇文章主要介紹了關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系,WeakHashMap從名字可以得知主要和Map有關(guān),不過還有一個Weak,我們就更能自然而然的想到這里面還牽扯到一種弱引用結(jié)構(gòu),因此想要徹底搞懂,我們還需要知道四種引用,需要的朋友可以參考下2023-09-09
詳解java CountDownLatch和CyclicBarrier在內(nèi)部實現(xiàn)和場景上的區(qū)別
這篇文章主要介紹了詳解java CountDownLatch和CyclicBarrier在內(nèi)部實現(xiàn)和場景上的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

