在平時開發項目時對數據的處理肯定是少不了的。對於數據處理也是很多程序員比較頭疼的問題,就比如項目中的數據是如何添加進去呢?一條一條的錄入?好像又有點不太友好,數據多了效率太低了,最關鍵的是甲方爸爸肯定不會滿意的。
這時我們可以使用POI來操作Excel表格,可以通過POI來把Excel中的數據批量導入到數據庫中,從而簡化操作,提高效率。反之我們還可以通過POI把數據庫中的數據導出到Excel表格中。
POI 簡介
Apache POI是Apache軟件基金會的開放源碼函式庫,POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。
POI依賴文件
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
POI常用類
類名 | 說明 |
---|---|
HSSFWorkbook | Excel的文件對象 |
HSSFSheet | Excel的表單 |
HSSFRow | Excel的行 |
HSSFCell | Excel的格子單元 |
HSSFFont | Excel字體 |
HSSFDataFormat | 格子單元的日期格式 |
HSSFHeader | Excel文檔Sheet的頁眉 |
HSSFFooter | Excel文檔Sheet的頁腳 |
樣式: | |
HSSFCellStyle | cell樣式 |
輔助操作包括: | |
HSSFDateUtil | 日期 |
HSSFPrintSetup | 打印 |
HSSFErrorConstants | 錯誤信息表 |
數據導出
首先,理解一下一個Excel的文件的組織形式,一個Excel文件對應於一個workbook(HSSFWorkbook),一個workbook可以有多個sheet(HSSFSheet)表單組成,一個sheet是由多個row(HSSFRow)行組成,一個row是由多個cell(HSSFCell)單元格組成。
所以對應的操作步驟如下:
- 用HSSFWorkbook打開或者創建Excel文件對象。
- 用HSSFWorkbook對象返回或者創建Sheet對象。
- 用Sheet對象返回行對象,用行對象得到Cell對象。
- 對Cell對象讀寫。
下面來看一下數據導出功能
前臺頁面只需要給出一個跳轉到後臺導出方法的鏈接即可。
<button class="layui-btn layui-btn-sm" onclick="window.location.href='staff/export'">導出</button>
後臺對應導出的方法,代碼如下:
@RequestMapping(value = "/export")
@ResponseBody
public void export(HttpServletRequest request, HttpServletResponse response) throws Exception {
//查詢數據庫中的數據
List<Staff> staffList = staffService.findAll();
//創建excel的文檔對象
HSSFWorkbook workbook = new HSSFWorkbook();
//創建員工信息工作薄,表單
HSSFSheet sheet = workbook.createSheet("員工信息");
//在sheet裏創建第一行,參數爲行索引(excel的行),從0開始
HSSFRow row1 = sheet.createRow(0);
//創建單元格excel的單元格,參數爲列索引,從0開始
HSSFCell cell = row1.createCell(0);
//設置單元格內容,標題第一行(可以不設置,根據項目需求)
cell.setCellValue("員工信息");
//合併單元格CellRangeAddress構造參數依次表示起始行,截至行,起始列, 截至列,一行標題合併單元格
sheet.addMergedRegion(new CellRangeAddress(0,0,0,3));
//設置列名(每列的小標題)
String[] fieldnames = {"姓名", "部門", "年齡", "工號"};
//在sheet裏創建第二行,第一行設置了標題
HSSFRow row2 = sheet.createRow(1);
for(int i = 0; i < fieldnames.length; i++) {
row2.createCell(i).setCellValue(fieldnames[i]); //設置列名
}
//填充導出的數據到Excel中
for(int i = 0; i < staffList.size(); i++){
HSSFRow rows = sheet.createRow(i + 2);
HSSFCell cellName = rows.createCell(0);
cellName.setCellValue(staffList.get(i).getName());
HSSFCell cell1Dept = rows.createCell(1);
cell1Dept.setCellValue(staffList.get(i).getDept());
HSSFCell cell1Age = rows.createCell(2);
cell1Age.setCellValue(staffList.get(i).getAge());
HSSFCell cell1Number = rows.createCell(3);
cell1Number.setCellValue(staffList.get(i).getNumber());
}
//輸出Excel文件
OutputStream output=response.getOutputStream();
response.reset();
//filename可以設置Excel文件的名稱
response.setHeader("Content-disposition", "attachment; filename=staff.xls");
response.setContentType("application/msexcel");
workbook.write(output);
output.close();
}
註釋怎麼清楚,就不用多解釋了吧,(#^ . ^#)。導出結果如下:
設置樣式
上面導出的內容大家也看到了,就是最原始的樣式。其實在導出時我們也可以對其Excel設置相應的樣式。
1、合併單元格
使用HSSFSheet類中的addMergedRegion(CellRangeAddress region)方法,上面導出也用到過。
參數CellRangeAddress表示合併的區域,方法如下:
public CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
其中參數依次表示起始行,截至行,起始列, 截至列。有興趣的小夥伴可以點進封裝類中看一下。
2、設置單元格的大小
HSSFSheet sheet = workbook.createSheet("員工信息");// 創建工作表(Sheet)
HSSFRow row = sheet.createRow(1);
HSSFCell cell = row.createCell(1);
cell.setCellValue("張三");
sheet.setColumnWidth(1, 256 * 50); //設置第一列的寬度,爲50個字符寬度
row.setHeightInPoints(50); //設置一行的高度
setColumnWidth方法和setHeightInPoints方法適合這是部分的樣式,如果需要設置全部樣式,可以使用HSSFSheet.setDefaultColumnWidth和HSSFSheet.setDefaultRowHeightInPoints方法設置默認的列寬和行高。
3、設置單元格樣式
單元格樣式是通過HSSFCellStyle類來設置的,所以我們需要先得到HSSFCellStyle 類
HSSFCellStyle style = workbook.createCellStyle()
3.1、設置水平對齊方式
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平居中
水平對齊相關參數
- 如果是左側對齊就是 HSSFCellStyle.ALIGN_FILL。
- 如果是居中對齊就是 HSSFCellStyle.ALIGN_CENTER。
- 如果是右側對齊就是 HSSFCellStyle.ALIGN_RIGHT。
- 如果是跨列舉中就是 HSSFCellStyle.ALIGN_CENTER_SELECTION。
- 如果是兩端對齊就是 HSSFCellStyle.ALIGN_JUSTIFY。
3.2、設置垂直對齊方式
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中
垂直對齊相關參數
- 如果是靠上就是 HSSFCellStyle.VERTICAL_TOP。
- 如果是居中就是 HSSFCellStyle.VERTICAL_CENTER。
- 如果是靠下就是 HSSFCellStyle.VERTICAL_BOTTOM。
- 如果是兩端對齊就是 HSSFCellStyle.VERTICAL_JUSTIFY。
3.3、設置邊框
style.setBorderTop(HSSFCellStyle.BORDER_DOTTED);//上邊框
style.setBorderBottom(HSSFCellStyle.BORDER_THICK);//下邊框
style.setBorderLeft(HSSFCellStyle.BORDER_DOUBLE);//左邊框
style.setBorderRight(HSSFCellStyle.BORDER_SLANTED_DASH_DOT);//右邊框
style.setTopBorderColor(HSSFColor.RED.index);//上邊框顏色
style.setBottomBorderColor(HSSFColor.BLUE.index);//下邊框顏色
style.setLeftBorderColor(HSSFColor.GREEN.index);//左邊框顏色
style.setRightBorderColor(HSSFColor.PINK.index);//右邊框顏色
3.4、應用樣式
//用在某一個單元格中
cell.setCellStyle(cellStyle);
//用在一行中,設置一行的樣式
row.setRowStyle(cellStyle);
4、設置字體樣式
字體樣式是通過HSSFFont類來設置的,所以我們需要先得到HSSFFont類
HSSFFont font = workbook.createFont()
設置樣式
font.setFontName("華文行楷");//設置字體名稱
font.setFontHeightInPoints((short)28);//設置字號
font.setColor(HSSFColor.RED.index);//設置字體顏色
font.setUnderline(FontFormatting.U_SINGLE);//設置下劃線
font.setTypeOffset(FontFormatting.SS_SUPER);//設置上標下標
font.setStrikeout(true);//設置刪除線
下劃線選項值:
- 單下劃線 FontFormatting.U_SINGLE
- 雙下劃線 FontFormatting.U_DOUBLE
- 會計用單下劃線 FontFormatting.U_SINGLE_ACCOUNTING
- 會計用雙下劃線 FontFormatting.U_DOUBLE_ACCOUNTING
- 無下劃線 FontFormatting.U_NONE
上標下標選項值:
- 上標 FontFormatting.SS_SUPER
- 下標 FontFormatting.SS_SUB
- 普通,默認值 FontFormatting.SS_NONE
應用樣式
//字體也是單元格格式的一部分,所以從屬於HSSFCellStyle
// 將字體對象賦值給單元格樣式對象
style.setFont(font);
// 將單元格樣式應用於單元格
cell.setCellStyle(cellStyle);
數據導入
導入數據時,頁面需要給出一個選擇文件的標籤,頁面就不多說了,只要有一個上傳的標籤即可。
這是用layui寫的一個簡單的頁面,代碼如下:
<style type="text/css">
#updateFile{
margin: 20px 0px 20px 50px;
}
</style>
<form id="importForm" method="post" class="layui-form" >
<div class="layui-form-item" style="margin: 0px;">
<button type="button" name="file" class="layui-btn layui-btn-normal" id="updateFile">選擇文件</button>
</div>
<div class="layui-form-item">
<div class="layui-input-block" style="margin-left: 50px;">
<input class="layui-btn layui-btn-normal" id="upload" value=" 導 入 " />
<a href="staff/exportmoban">下載模板</a>
</div>
</div>
</form>
<script type="text/javascript">
layui.use(['form', 'upload'], function(){
var upload = layui.upload;
upload.render({
elem: '#updateFile',
url: 'staff/excelimport',
auto: false,
accept: 'file',
exts: 'xls|excel|xlsx',
bindAction: '#upload',
done: function(res){ //導出成功後回調
}
});
});
</script>
其中下載模板和導出數據基本一樣,只需要創建一個Excel給出一條樣式數據即可,代碼如下:
@RequestMapping(value = "exportmoban")
@ResponseBody
public void exportmoban(HttpServletRequest request, HttpServletResponse response) throws Exception{
//創建excel的文檔對象
HSSFWorkbook workbook = new HSSFWorkbook();
//創建員工信息工作薄,表單
HSSFSheet sheet = workbook.createSheet("員工信息");
//在sheet裏創建第一行,參數爲行索引(excel的行),從0開始
HSSFRow row1 = sheet.createRow(0);
//創建單元格excel的單元格,參數爲列索引,從0開始
HSSFCell cell = row1.createCell(0);
//設置單元格內容,標題第一行(可以不設置,根據項目需求)
cell.setCellValue("員工信息");
//合併單元格CellRangeAddress構造參數依次表示起始行,截至行,起始列, 截至列,一行標題合併單元格
sheet.addMergedRegion(new CellRangeAddress(0,0,0,3));
//設置列名(每列的小標題)
String[] fieldnames = {"姓名", "部門", "年齡", "工號"};
//在sheet裏創建第二行,第一行設置了標題
HSSFRow row2 = sheet.createRow(1);
for(int i = 0; i < fieldnames.length; i++) {
row2.createCell(i).setCellValue(fieldnames[i]); //設置列名
}
//模板數據
HSSFRow row3 = sheet.createRow(2);
row3.createCell(0).setCellValue("王五");
row3.createCell(1).setCellValue("軟件部");
row3.createCell(2).setCellValue(“18“);
row3.createCell(3).setCellValue("003");
//輸出Excel文件
OutputStream output=response.getOutputStream();
response.reset();
//filename可以設置Excel文件的名稱
response.setHeader("Content-disposition", "attachment; filename=staff.xls");
response.setContentType("application/msexcel");
workbook.write(output);
output.close();
}
模板下載如圖。
導入數據,我們只需要選擇一個有數據的Excel表格(數據格式需要和模板中格式一樣),點擊導入即可。
後臺對應導入數據的excelimport方法,代碼如下:
@RequestMapping(value = "/excelimport")
@ResponseBody
public boolean excelimport(MultipartFile file) {
// 實例化工具類
ImportExcel excelReader = new ImportExcel();
try {
InputStream is = file.getInputStream();
// 導入excel
excelReader.readExcelContent(is);
Map<Integer, String> map = excelReader.readExcelContent(is);
//遍歷數據保存
//因爲第一行和第二行是標題,所以從2開始
for (int i = 2; i <= map.size() + 1; i++) {
String[] scoreArray = map.get(i).split("-");
if (scoreArray.length > 0) {
String name = scoreArray[0];
String dept = scoreArray[1];
Integer age = Integer.parseInt(scoreArray[2]);
String number = scoreArray[3];
Staff staff = new Staff();
staff.setName(name);
staff.setDept(dept);
staff.setAge(age);
staff.setNumber(number);
//保存
staffService.save(staff);
}
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
注: 如果對數據需要做一些效驗的話可以放在for循環中處理,這裏只是寫一個小案例,沒有添加任何效驗,根據項目需求自己添加。
其中ImportExcel類是一個處理導入的Excel數據的封裝類。代碼如下:
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class ImportExcel {
private POIFSFileSystem fs;
private HSSFWorkbook wb;
private HSSFSheet sheet;
private HSSFRow row;
/**
* 讀取Excel表格表頭的內容
* @param is
* @return String 表頭內容的數組
*/
public String[] readExcelTitle(InputStream is) {
try {
fs = new POIFSFileSystem(is);
wb = new HSSFWorkbook(fs);
} catch (IOException e) {
e.printStackTrace();
}
sheet = wb.getSheetAt(0);
//得到首行的row
row = sheet.getRow(0);
// 標題總列數
int colNum = row.getPhysicalNumberOfCells();
String[] title = new String[colNum];
for (int i = 0; i < colNum; i++) {
title[i] = getCellFormatValue(row.getCell((short) i));
}
return title;
}
/**
* 讀取Excel數據內容
* @param is
* @return Map 包含單元格數據內容的Map對象
*/
public Map<Integer, String> readExcelContent(InputStream is) {
Map<Integer, String> content = new HashMap<Integer, String>();
String str = "";
try {
fs = new POIFSFileSystem(is);
wb = new HSSFWorkbook(fs);
} catch (IOException e) {
//e.printStackTrace();
}
sheet = wb.getSheetAt(0);
// 得到總行數
int rowNum = sheet.getLastRowNum();
//由於第0行和第一行已經合併了 在這裏索引從2開始
row = sheet.getRow(2);
int colNum = row.getPhysicalNumberOfCells();
// 正文內容應該從第二行開始,第一行爲表頭的標題
for (int i = 2; i <= rowNum; i++) {
row = sheet.getRow(i);
int j = 0;
while (j < colNum) {
str += getCellFormatValue(row.getCell((short) j)).trim() + "-";
j++;
}
content.put(i, str);
str = "";
}
return content;
}
/**
* 獲取單元格數據內容爲字符串類型的數據
*
* @param cell Excel單元格
* @return String 單元格數據內容
*/
private String getStringCellValue(HSSFCell cell) {
String strCell = "";
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_STRING:
strCell = cell.getStringCellValue();
break;
case HSSFCell.CELL_TYPE_NUMERIC:
strCell = String.valueOf(cell.getNumericCellValue());
break;
case HSSFCell.CELL_TYPE_BOOLEAN:
strCell = String.valueOf(cell.getBooleanCellValue());
break;
case HSSFCell.CELL_TYPE_BLANK:
strCell = "";
break;
default:
strCell = "";
break;
}
if (strCell.equals("") || strCell == null) {
return "";
}
if (cell == null) {
return "";
}
return strCell;
}
/**
* 獲取單元格數據內容爲日期類型的數據
*
* @param cell
* Excel單元格
* @return String 單元格數據內容
*/
private String getDateCellValue(HSSFCell cell) {
String result = "";
try {
int cellType = cell.getCellType();
if (cellType == HSSFCell.CELL_TYPE_NUMERIC) {
Date date = cell.getDateCellValue();
result = (date.getYear() + 1900) + "-" + (date.getMonth() + 1)
+ "-" + date.getDate();
} else if (cellType == HSSFCell.CELL_TYPE_STRING) {
String date = getStringCellValue(cell);
result = date.replaceAll("[年月]", "-").replace("日", "").trim();
} else if (cellType == HSSFCell.CELL_TYPE_BLANK) {
result = "";
}
} catch (Exception e) {
System.out.println("日期格式不正確!");
e.printStackTrace();
}
return result;
}
/**
* 根據HSSFCell類型設置數據
* @param cell
* @return
*/
private String getCellFormatValue(HSSFCell cell) {
String cellvalue = "";
if (cell != null) {
// 判斷當前Cell的Type
switch (cell.getCellType()) {
// 如果當前Cell的Type爲NUMERIC
case HSSFCell.CELL_TYPE_NUMERIC:
case HSSFCell.CELL_TYPE_FORMULA: {
// 判斷當前的cell是否爲Date
if (HSSFDateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
cellvalue = sdf.format(date);
}
// 如果是純數字
else {
// 取得當前Cell的數值
cellvalue = String.valueOf(cell.getNumericCellValue());
}
break;
}
// 如果當前Cell的Type爲STRIN
case HSSFCell.CELL_TYPE_STRING:
// 取得當前的Cell字符串
cellvalue = cell.getRichStringCellValue().getString();
break;
// 默認的Cell值
default:
cellvalue = " ";
}
} else {
cellvalue = "";
}
return cellvalue;
}
}
導入的數據:
都看到這裏了,點個贊在走唄!原創不易,各位的支持和認可,是對我創作的最大鼓勵。