Java 實現 Excel 與數據庫的數據轉換

 

一、準備

(一)環境

需要用到下面兩個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 或者下載下來導入即可。

github:https://github.com/yyzheng1729/excelData

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章