一、準備
(一)環境
需要用到下面兩個jar包,可以自行到網上下載或者從我提供的百度雲鏈接下載。
鏈接: https://pan.baidu.com/s/1pFLM7VEKM4WNSQ3FBbHKXQ 提取碼: bhje
(二)數據庫
二、數據庫中的數據導入 Excel
(一)原理
通過調用工具類,先判斷在服務器中指定的文件夾中有沒有存在同名的 excle 表,有的話就先刪除掉,沒有的話,就在指定的文件夾中生成一份新的 excle 表格,再調用瀏覽器的下載接口,把 excle 表下載到自己電腦上的指定位置,然後刪除掉服務器上的 excle 表格。
(二)源碼
1. MakeExcel:自定義的工具類
package com.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import jxl.Sheet;
import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.VerticalAlignment;
import jxl.read.biff.BiffException;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
public class MakeExcel {
/**
* 創建 excel 表格
*
* @param list
* 一條數據存一個map對象,map對象中存列和值得對應關係
* @param destFile(目標文件)
* 當然就是要存的文件信息,即表格保存的路徑
* @param headList
* 很重要,它是列的展示,當然和數據的列要對應不然找不到對應的地方存儲
* @param message
* 表格第一行的表頭信息
* @throws WriteException
* @throws IOException
*/
public static void CreateExcelFile(List<Map<String, Object>> list, File destFile, List<String> headList,
String message) throws WriteException, IOException {
// 獲取傳入 list 的大小,即有多少條數據
int sizeAll = list.size();
// 設置每頁最大條數 65534 ,求出整數頁 wholeNumber
int wholeNumber = sizeAll / 65534;
// 求出最後一頁的條數
int yu = sizeAll % 65534;
// sheetSize:表示頁數
// flagList:循環參數,後面輸出數據的時候用到
int sheetSize = 1;
int flagList = 1;
// 判斷要導出的數據需要存放在幾頁的 excel 表上
if (sizeAll <= 65534) {
sheetSize = 1;
} else {
if (yu > 0) {
sheetSize = wholeNumber + 1;
} else {
sheetSize = wholeNumber;
}
}
// 用 WritableWorkbook 創建一個可讀寫的工作簿
WritableWorkbook book = null;
// 以 destFile 爲文件名來創建一個 workbook
// createWorkbook() : 參數 destFile 爲 new File(D:/example.xls)
book = Workbook.createWorkbook(destFile);
//
if (list.size() == 0) {
// list 中沒有數據,直接操作空的工作簿
// 寫進文檔
book.write();
} else {
for (int j = 0; j < sheetSize; j++) {
int index;
System.out.println("*****sheet(excle的左下角)" + j + "*****");
// 創建工作表( excel 中的 sheet 表)
// createSheet(String str,int n)
// String 型參數 str 爲 sheet 表的名字,一般命名爲"sheet0"或"sheet1"即可
// int 型參數 n 代表sheet號,0是第一頁,1是第二頁,依次類推,打開Excel表格在底端可以看到
WritableSheet sheet = book.createSheet(destFile.getName().replace(".xls", "") + j, j);
// ARIAL : 字體樣式 【WritableFont.createFont("宋體") : 宋體字體的設置】
// WritableFont.TIMES
// 19 :字體大小
// WritableFont.BOLD, false 是判斷是否爲斜體,選擇true時爲斜體 ,默認爲 false
WritableFont BoldFont = new WritableFont(WritableFont.createFont("宋體"), 18);
WritableCellFormat wcf_center = new WritableCellFormat(BoldFont);
wcf_center.setBorder(Border.ALL, BorderLineStyle.THIN); // 線條
wcf_center.setVerticalAlignment(VerticalAlignment.CENTRE); // 文字垂直對齊
wcf_center.setAlignment(Alignment.CENTRE); // 文字水平對齊
wcf_center.setWrap(false); // 文字是否換行
// wcf_center.setBackground(Colour.LIGHT_GREEN);// 單元格背景顏色
for (int i = 0; i < headList.size() + 1; i++) {
sheet.setColumnView(i, 20);// 設置第i列的寬度
}
// 合併首行,設置首行信息
// Label():第一個參數表示第幾列,第二個參數表示第幾行
// Label(0, 0, message,wcf_center):表示第0列第0行
// message:填入表格的信息
// wcf_center:表格樣式
sheet.mergeCells(0, 0, headList.size() - 1, 0);
sheet.addCell(new Label(0, 0, message, wcf_center));
// 輸出列名
index = 0;
for (String name : headList) {
// 上面合併了首行,所有這裏在第二行開始輸出列名
sheet.addCell(new Label(index, 1, name, wcf_center));
index++;
}
// 輸出數據
// i:表示輸出的數據爲第 i 條
// t:表示在 excle 中的第 t+1 行輸出數據
int i = 0;
int t = 2;
// flagList 初始值爲1,作爲循環變量
// 當 flagList 大於需要輸出的數據條數時,則終止循環
while (flagList <= list.size()) {
index = 0;
if (i < 65534) {
for (String name : headList) {
sheet.addCell(new Label(index, t, list.get(flagList - 1).get(name) + "", wcf_center));
index++;
}
i++;
t++;
flagList++;
} else {
break;
}
}
}
}
// 寫入文檔
book.write();
if (book != null) {
// 關閉Excel工作簿對象
book.close();
}
}
/**
* 調用瀏覽器接口進行文件下載
*
* @param filepath
* 文件路徑
* @param response
*/
public static void send(String filepath, HttpServletResponse response) {
try {
File file = new File(filepath);// filepath 是文件地址
String filename = file.getName();// 獲取日誌文件名稱
InputStream fis = new BufferedInputStream(new FileInputStream(filepath));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
// 先去掉文件名稱中的空格,然後轉換編碼格式爲utf-8,保證不出現亂碼,這個文件名稱用於瀏覽器的下載框中自動顯示的文件名
response.addHeader("Content-Disposition",
"attachment;filename=" + new String(filename.replaceAll(" ", "").getBytes("utf-8"), "iso8859-1"));
response.addHeader("Content-Length", "" + file.length());
OutputStream os = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
os.write(buffer);// 輸出文件
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 刪除單個文件
*
* @param sPath
* 被刪除文件的文件名
* @return 單個文件刪除成功返回true,否則返回false
*/
public static boolean deleteFile(String sPath) {
boolean flag = false;
File file = new File(sPath);
// 路徑爲文件且不爲空則進行刪除
if (file.isFile() && file.exists()) {
file.delete();
flag = true;
}
return flag;
}
}
2. Controller 層操作
/**
* 下載用戶 excel 表接口
*
* @param response
* @throws IOException
*/
@RequestMapping("/download")
public void download(HttpServletResponse response) throws IOException {
// 指定一個地方臨時存放生成的 excel 文件,然後後面調用瀏覽器接口下載完後再刪除
String FILEPATH = "d:/test.xls";
// 判斷 "c:/test.xls" 文件是否已經存在,如果存在就刪除掉
MakeExcel.deleteFile(FILEPATH);
// 首行表頭信息
List<String> ll = new ArrayList<>();
ll.add("用戶ID");
ll.add("姓名");
ll.add("電話");
// 獲取所有用戶信息
List<User> allUserList = userService.getUserList();
// 將用戶的相關信息遍歷到 List<Map<String, Object>> 中
List<Map<String, Object>> list = new ArrayList<>();
for (User user : allUserList) {
Map<String, Object> map = new HashMap<>();
map.put("用戶ID", user.getId());
map.put("姓名", user.getName());
map.put("電話", user.getPhone());
list.add(map);
}
try {
// 第一個參數:表格中的數據
// 第二個參數:表格保存的路徑
// 第三個參數:表格第二行的列信息
// 第四個參數:表格第一行的表頭信息
// 參照效果圖看會清楚些
MakeExcel.CreateExcelFile(list, new File(FILEPATH), ll, "用戶表");
} catch (WriteException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 調用瀏覽器下載接口
MakeExcel.send(FILEPATH, response);
// 刪除臨時存放的 excel 文件
boolean deleteFileState = MakeExcel.deleteFile(FILEPATH);
if (deleteFileState) {
System.out.println("服務器上文件刪除成功!!!");
} else {
System.out.println("服務器上文件刪除失敗!!!");
}
}
3. service 層和 dao 層
根據 controller 層中的提示自己寫,主要是對數據庫的操作。
4. JSP 頁面
<form action="download">
<button type="submit">下載文件</button>
</form>
(三)測試
數據庫中的數據表:
數據庫中的數據導入到 excel 表中的效果:
三、Excel 中的數據導入數據庫
(一)原理
在頁面上選中要上傳的文件,點擊上傳按鈕。後臺接收到上傳的 excel 文件,先臨時存放到指定的路徑,然後再讀取臨時存放的 excel 文件中數據,讀取一條插入一條,或者可以把 excel 中的數據,全部讀取出來放到 list 裏面,再執行插入數據庫操作,等 excel 數據都導入到數據庫中後,再把臨時存放的 excel 文件給刪除掉即可。
提示:在插入數據前,可以檢驗當前這條數據是否已經存在數據庫中,如果存在可以進行更新數據操作。
問:爲什麼要把 excel 文件臨時存放起來?
答:因爲瀏覽器的願意,無法獲取到上傳文件的絕對路徑。
(二)源碼
1. Controller 層操作
/**
* 從 excel 中添加數據到數據庫中
*
* @param filename
* @param request
* @throws IOException
* @throws BiffException
*/
@RequestMapping(value = "/getexcelfile", produces = "application/json;charset=utf-8")
@ResponseBody
public String getExcelFile(MultipartFile file, HttpServletRequest request) throws IOException, BiffException {
/** 臨時存放 excel 文件 **/
// 設置相對路徑
String realPath = request.getSession().getServletContext().getRealPath("/upload");
System.out.println(" realPath:" + realPath);
// 存放 excel 文件的絕對路徑
String uploadPath = "";
// 獲取文件的格式
String extention = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
// 對格式進行篩選
// 僅支持上傳 excel 文件,xls 或 xlsx
if (extention.equalsIgnoreCase("xls") || extention.equalsIgnoreCase("xlsx")) {
File f = new File(realPath);// 在路徑下創建文件夾
String fileName = file.getOriginalFilename();// 獲取上傳文件原命名
uploadPath = realPath + File.separator + fileName;// 拼接上傳路徑
System.out.println(" uploadPath:" + uploadPath);
// 如果指定文件 upload 不存在,則先新建文件夾
if (!f.exists()) {
f.mkdirs();
}
file.transferTo(new File(uploadPath));// 文件的傳輸
} else {
System.out.println("上傳文件格式不對!");
}
/** 讀取臨時存放的 excel 文件,並將數據導入數據庫 **/
// 獲取 excel 表的文件流
File excelFile = new File(uploadPath);
Workbook book = Workbook.getWorkbook(excelFile);
// 獲取 sheet 表
// 可以使用下面的寫法,"test0" 表示 sheet 表的名字
// Sheet rs = book.getSheet("test0");
// getSheet(0):表示獲取第一張 sheet 表(從左到右數起)
Sheet rs = book.getSheet(0);
int columns = rs.getColumns();// 得到所有的列
int rows = rs.getRows();// 得到所有的行
System.out.println(" columns:" + columns + " rows:" + rows);
List<User> list = new ArrayList<>();// 臨時存放 excel 數據
/** 將 excel 數據存放到 List<User> **/
// i=2:表示第三行
for (int i = 2; i < rows; i++) {
User user = new User();
// j=0:表示第一列
// excel 中默認左邊編號也算一列,所以需要從第二列開始獲取數據,則下面都使用 j++
for (int j = 0; j < columns; j++) {
String id = rs.getCell(j++, i).getContents();
String name = rs.getCell(j++, i).getContents();
String phone = rs.getCell(j++, i).getContents();
System.out.println("id=" + id);
System.out.println("name=" + name);
System.out.println("phone=" + phone);
// 將數據 set 進 user 對象中
user.setId(Integer.parseInt(id));
user.setName(name);
user.setPhone(phone);
// 將 user 對象添加到 List<User> 裏
list.add(user);
}
}
/** 將 List<User> 中的數據插入或者更新到數據庫 **/
User userOne = new User();
User tempUser = new User();
for (int k = 0; k < list.size(); k++) {
userOne = list.get(k);
tempUser = userService.selectUserById(userOne.getId());
if (tempUser != null) {
// 查詢返回值 tempUser 不爲空,則說明當前這條信息已經存在數據庫
// 進行數據更新操作即可
userService.updataUserByKey(userOne);
} else if (tempUser == null) {
// tempUser 爲空,數據庫沒有當前信息
// 將數據插入即可
userService.insertUser(userOne);
}
}
/** 刪除臨時存放的 excel 文件 **/
boolean deleteFileState = MakeExcel.deleteFile(realPath + "/test.xls");
if (deleteFileState) {
System.out.println("服務器上文件刪除成功!!!");
} else {
System.out.println("服務器上文件刪除失敗!!!");
}
return " excel 數據更新到數據庫成功";
}
2. service 層和 dao 層
根據 controller 層中的提示自己寫,主要是對數據的更新和插入操作。
3. JSP 頁面
<form action="getexcelfile">
<input type="file" name="filename" enctype="multipart/form-data" method="post">
<button type="submit">提交</button>
</form>
(三)測試
我上面 excel 數據插入的代碼是基於第一部分生成的 excel 格式來寫的。所以上傳 excel 文件可以使用剛剛下載下來的那一個。然後直接修改或者新增數據即可。如下圖所示。
數據庫中表的數據在導入 excel 數據前的前後對比圖:左邊是導入數據前,右邊是導入數據後。
可以看到,如果數據已經存在,則直接更新數據,如果不存在則插入新的數據。
四、Demo
開發工具:Eclipse
直接 clone 或者下載下來導入即可。