目前網頁導出文件的功能比較常見並且實用,所以個人也遇到了導出Excel表格的需求。關於導出表格數據,其實前端也能進行導出操作,不過一般前端進行的導出操作適用於數據量少的時候,普通來說,數據條數少於600行,前端導出效率也能行;如果數據量偏大,那麼後端導出是最好的選擇。
對於後端導出,請求類型筆者是用的GET請求,因爲使用POST請求,導出數據會是亂碼,也不能導出到文件,至於原因也沒有進行深究,如果有哪位大佬瞭解,煩請告知。
因爲需要導出的表格比較多,而每個表格的數據模型又是不同的模型,所以在這裏,筆者採用了Java泛型技術來達到導出方法的通用性。話不多說,下面就是整個導出方法的代碼。
爲了便於讀者閱讀,註釋比較多,並且筆者在這裏先舉一個實例,方便讀者理解。
一、前端導出頁面
姓名 | 工號 | 性別 | 年齡 | 工資 |
張三 | 1 | 男 | 19 | 1000 |
李四 | 2 | 女 | 11 | 1234 |
比如前端有一個這樣的表格,當然,真正的肯定不是這種簡陋的,這只是方便說明隨便提供的表格。
這張表格的數據其實可以分爲兩部分:一部分是表頭信息(下面代碼中的表頭信息,tableHeaders參數的value),就是如姓名、工號、性別這種,指示了這列數據的含義;二部分就是一條條數據了(下面代碼中的List<T> datas參數)。這些數據,其實是後端返回的一個List<PeopleInfo>集合,每一條數據就是一個PeopleInfo對象。因爲數據模型PeopleInfo的屬性不會用中文表示,所以,爲了將數據模型的每個字段對應上表頭的信息字段,就用到了KeyValueVO這個模型,這個模型的key就是數據模型的字段,value就是表頭的字段。
這裏,用戶可以選擇是否顯示某一列,比如,我不想看工資,年齡,性別,我只關注員工的姓名和工號,那麼在前端,用戶可以設置這幾列不可見,對應的這種設置就會保存在後端數據庫中。這個時候用戶再導出數據,就只會導出員工的姓名和工號了,其他數據就不會導出了(對應下面代碼的需要導出的字段exportFields)
所以,最後,當用戶點擊導出按鈕時,前端會傳一個請求過來,到時候,後端經過搜索,將需要導出的數據,需要導出的字段,以及數據模型和表頭字段的對應關係當做參數傳入方法,這樣就能導出數據了,並且導出的excel也會有表頭信息,即:姓名、工號這種信息。
1.使用的模型類
package sora.test.exampl;
/**
* 鍵值對模型
* @author sora
*
*/
public class KeyValueVO {
private String key;
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
2.到處Excel泛型方法
package sora.test.exampl;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import org.ietf.jgss.Oid;
/**
* Excel文件導出工具類
* 因爲環境限制,有一些類沒有導入進來,望讀者注意!!!!!!
* @author sora
*
*/
public class ExcelExportUtil {
private static final int TWO_HUNDRED_FIFTY_SIX = 256;
private static final int EIGHT = 8;
private static final int FOUR = 4;
private static final int THREE = 3;
private static final int TWO = 2;
/**
* 導出excel表格數據
* @param datas 需要導出的數據模型集合
* @param exportFields 需要導出的字段顯示
* @param excelName 導出Excel文件名
* @param tableHeaders 表格數據模型與表頭信息鍵值對
* @param response 響應
*/
public static <T> void excelExport(List<T> datas, List<String> exportFields, String excelName, List<KeyValueVO> tableHeaders, HttpServletResponse response)
{
if (null == datas)
{
return;
}
try
{
excelName = new String(excelName.getBytes("UTF-8"),"ISO-8859-1");
}
catch (Exception e1)
{
e1.printStackTrace();
}
response.setCharacterEncoding("utf-8");
try
{
writeExcel(datas,exportFields,excelName,tableHeaders,response);
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 寫入數據
* @param datas 數據
* @param exportFields 導出字段
* @param excelName 導出Excel名字
* @param tableHeaders 表頭信息
* @param response 響應
* @throws Exception
*/
public static <T> void writeExcel(List<T> datas, List<String> exportFields, String excelName, List<KeyValueVO> tableHeaders, HttpServletResponse response)
throws Exception
{
//創建一個excel文件
HSSFWorkbook excel = new HSSFWorkbook();
//excel文件裏面一張表格sheet
HSSFSheet excelSheet = excel.createSheet("sheet1");
//sheet中的行
HSSFRow excelRow = null;
//sheet中的單元格
HSSFCell excelCell = null;
//行下標,因爲表頭是用單獨的方法寫,所以直接從第二行開始寫數據
Integer rowSubScript = 1;
//單元格下標
Integer cellSubScript = 0;
//獲取數據模型的類對象
Class<?> clazz = datas.get(0).getClass();
//獲取模型聲明的所有字段
Field[] modelFields = clazz.getDeclaredFields();
exportFields = writeExcelFirstRowAndField(modelFields, exportFields, excelSheet, excelCell, tableHeaders);
for (T data : datas)
{
cellSubScript = 0;
excelRow = excelSheet.createRow(rowSubScript);
for (String exportField : exportFields)
{
excelCell = excelRow.createCell(cellSubScript);
String methodName = getInitialsUp(exportField);
Method getMethod = null;
getMethod = clazz.getMethod("get" + methodName);
setExcelCellValue(getMethod, data, excelCell);
cellSubScript++;
}
rowSubScript++;
}
OutputStream output = response.getOutPutStream();
excel.write(output);
output.flush();
output.close();
excel.close();
}
/**
* 寫入sheet表格第一行表頭信息,支持中文表頭
* @param modelFields 數據模型的字段
* @param exportFields 需要導出的表頭字段
* @param excelSheet sheet
* @param excelCell 單元格
* @param tableHeaders 表頭與模型字段的鍵值對
* @return List<Stirng> 實際需要導出的字段:因爲用戶可以設置某些字段不導出
* @throws Exception
*/
public static List<String> writeExcelFirstRowAndField(Field[] modelFields, List<String> exportFields, HSSFSheet excelSheet, HSSFCell excelCell,
List<KeyValueVO> tableHeaders)
throws Exception
{
HSSFRow excelRow = excelSheet.createRow(0);
Integer cellSubScript = 0;
List<String> actualExprtFields = new LinkedList<>();
//如果導出字段爲Null或者爲空,那麼默認導出數據模型的所有字段
if (null == exportFields || exportFields.isEmpty())
{
for (Field modelField : modelFields)
{
//如果這個字段在數據模型中的聲明是靜態的,即static,那麼就是不需要導出的字段
if (EIGHT == modelField.getModifiers())
{
continue;
}
excelCell = excelRow.createCell(cellSubScript);
String resultName = fieldToTableHeader(tableHeaders, modelField.getName());
if (resultName.isEmpty())
{
continue;
}
excelSheet.setCloumnWidth(cellSubScript,
resultName.getBytes("UTF-8").length * TWO * TWO_HUNDRED_FIFTY_SIX / THREE + FOUR * TWO_HUNDRED_FIFTY_SIX);
excelCell.setCellValue(resultName);
actualExprtFields.add(modelField.getName());
cellSubScript++;
}
}
else
{
for (String exportField : exportFields)
{
excelCell = excelRow.createCell(cellSubScript);
String resultName = fieldToTableHeader(tableHeaders, modelField.getName());
if (resultName.isEmpty())
{
continue;
}
excelSheet.setCloumnWidth(cellSubScript,
resultName.getBytes("UTF-8").length * TWO * TWO_HUNDRED_FIFTY_SIX / THREE + FOUR * TWO_HUNDRED_FIFTY_SIX);
excelCell.setCellValue(resultName);
actualExprtFields.add(modelField.getName());
cellSubScript++;
}
}
return actualExprtFields;
}
/**
* 設置單元格的值
* @param getMethod 類對象屬性的get方法
* @param data 數據
* @param excelCell 單元格
*/
public static <T> void setExcelCellValue(Method getMethod, T data, HSSFCell excelCell)
{
try
{
if (null == getMethod.invoke(data))
{
excelCell.setCellValue("");
}
else
{
excelCell.setCellValue(getMethod.invoke(data).toString());
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 返回數據模型字段對應的表頭字段
* @param tableHeaders 表頭鍵值對
* @param modelField 模型字段
* @return 目標字符串
*/
public static String fieldToTableHeader(List<KeyValueVO> tableHeaders, String modelField)
{
for (KeyValueVO tableHeader : tableHeaders)
{
if (modelField.equals(tableHeader.getKey()))
{
return tableHeader.getValue();
}
}
return "";
}
/**
* 將字符串首字母大寫,如:big → Big
* @param field 字符串
* @return 首字母大寫之後的字符串
*/
public static String getInitialsUp(String field)
{
byte[] items = null;
try
{
items = field.getBytes("UTF-8");
items[0] = (byte) ((char)items[0] - 'a' + 'A');
return new String(items, "UTF-8");
}
catch (Exception e)
{
e.printStackTrace();
return "";
}
}
}