使用自定義註解+POI實現通用的表格匯入匯出
阿新 • • 發佈:2019-01-06
之前做過單表格的匯入匯出,現在由於專案的需要,對很多表都要執行匯入匯出功能。如果每張表都單獨去寫匯入匯出,未免太麻煩了一些,後來考慮做一個通用的匯入匯出。
1 首先要實現通用的匯出功能
匯出可以根據資料庫中表的欄位來匯出,至於哪些欄位需要匯出,哪些不需要,可以通過註解來實現。
(1)首先定義匯入匯出的註解
/**
* 匯入匯出配置
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
//@Documented
//@Component
public @interface ExcelConfig {
/**
* @return 表頭顯示名(如:id欄位顯示為"編號") 預設為欄位名
*/
String value() default "field";
/**
* @return 單元格寬度 預設-1(自動計算列寬)
*/
short width() default -1;
/**
* 將單元格值進行轉換後再匯出:<br/>
* 目前支援以下幾種場景:<br/>
* 1. 固定的數值轉換為字串值(如:1代表男,2代表女)<br/>
* <b>表示式:</b> "s:1=男,2=女"<br/>
*
* 2. 數值對應的值需要查詢資料庫才能進行對映(實現org.wuwz.poi.convert.ExportConvert介面)<br/>
* <b>表示式:</b> "c:org.wuwz.poi.test.GradeIdConvert"
*
* @return 預設不啟用
*/
String convert() default "";
/**
* @return 當前單元格的字型顏色 (預設 HSSFColor.BLACK.index)
*/
short color() default HSSFColor.BLACK.index;
/**
* 將單元格的值替換為當前配置的值:<br/>
* 應用場景: <br/>
* 密碼欄位匯出為:"******"
*
* @return 預設true
*/
String replace() default "";
int colIndex() default -1;//排序
boolean nullable() default true;//可以為空?
boolean isExportData() default true;//是否匯出
int formatTime() default 0;//是否時間轉換,是的話由時間字串轉時間戳
}
這裡定義了一些基本的值,都有註釋,自己去看。
(2)然後在需要執行匯出功能的類中加上該註解
@ExcelConfig(value = "商品名稱")
private String goods_name;
@ExcelConfig(value = "品牌ID",width = 100)
private Integer brand_id;
(3)下面我們定義一個工具類,專門用於處理匯入匯出功能
public class ExcelKit {
private Class<?> mClass = null;
private HttpServletResponse mResponse = null;
// 預設以此值填充空單元格,可通過 setEmptyCellValue(string)改變其預設值。
private String mEmptyCellValue = null;
// 分Sheet機制:每個Sheet最多多少條資料
private Integer mMaxSheetRecords = 10000;
// 快取資料格式器例項,避免多次使用反射進行例項化
private Map<String, ExcelConvert> mConvertInstanceCache = new HashMap<>();
protected ExcelKit() {
}
protected ExcelKit(Class<?> clazz) {
this(clazz, null);
}
protected ExcelKit(Class<?> clazz, HttpServletResponse response) {
this.mResponse = response;
this.mClass = clazz;
}
/**
* 用於瀏覽器匯出
*
* @param clazz 實體Class物件
* @param response 原生HttpServletResponse物件
* @return ExcelKit
*/
public static ExcelKit $Export(Class<?> clazz, HttpServletResponse response) {
return new ExcelKit(clazz, response);
}
/**
* 匯出Excel(此方式需依賴瀏覽器實現檔案下載,故應先使用$Export()構造器)
*
* @param data 資料集合
* @param sheetName 工作表名字
* @return true-操作成功,false-操作失敗
*/
public boolean toExcel(List<?> data, String sheetName) {
required$ExportParams();
try {
return toExcel(data, sheetName, mResponse.getOutputStream());
} catch (IOException e) {
log.error("匯出Excel失敗:" + e.getMessage(), e);
}
return false;
}
/**
* 針對轉換方法的預設實現(提供預設樣式和檔案命名規則)
*
* @param data 資料集合
* @param sheetName 工作表名字
* @param out 輸出流
* @return true-操作成功,false-操作失敗
*/
public boolean toExcel(List<?> data, String sheetName, OutputStream out) {
return toExcel(data, sheetName, new ExportHandler() {
@Override
public CellStyle headCellStyle(SXSSFWorkbook wb) {
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
cellStyle.setFillForegroundColor((short) 12);
cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);// 填充模式
cellStyle.setBorderTop(CellStyle.BORDER_THIN);// 上邊框為細邊框
cellStyle.setBorderRight(CellStyle.BORDER_THIN);// 右邊框為細邊框
cellStyle.setBorderBottom(CellStyle.BORDER_THIN);// 下邊框為細邊框
cellStyle.setBorderLeft(CellStyle.BORDER_THIN);// 左邊框為細邊框
cellStyle.setAlignment(CellStyle.ALIGN_LEFT);// 對齊
cellStyle.setFillForegroundColor(HSSFColor.GREEN.index);
cellStyle.setFillBackgroundColor(HSSFColor.GREEN.index);
font.setBoldweight(Font.BOLDWEIGHT_NORMAL);
// font.setFontHeightInPoints((short) 12);// 字型大小
font.setColor(HSSFColor.WHITE.index);
// 應用標題字型到標題樣式
cellStyle.setFont(font);
return cellStyle;
}
@Override
public String exportFileName(String sheetName) {
return String.format("匯出-%s-%s", sheetName, System.currentTimeMillis());
}
}, out);
}
public boolean toExcel(List<?> data, String sheetName, ExportHandler handler, OutputStream out) {
required$BuilderParams();
long begin = System.currentTimeMillis();
// if (data == null || data.size() < 1) {
// log.error("沒有檢測到資料,不執行匯出操作。");
// return false;
// }
if (data == null) {
log.error("沒有檢測到資料,不執行匯出操作。");
return false;
}
log.info(String.format("即將匯出excel資料:%s條,請稍後..", data.size()));
// 匯出列查詢。
ExcelConfig currentExportConfig = null;
ExcelItem currentExportItem = null;
List<ExcelItem> exportItems = new ArrayList<>();
for (Field field : mClass.getDeclaredFields()) {
currentExportConfig = field.getAnnotation(ExcelConfig.class);
if (currentExportConfig != null&¤tExportConfig.isExportData()) {
currentExportItem = new ExcelItem()
.setField(field.getName())
.setDisplay("field".equals(currentExportConfig.value()) ? field.getName() : currentExportConfig.value())
.setWidth(currentExportConfig.width())
.setConvert(currentExportConfig.convert())
.setColor(currentExportConfig.color())
.setReplace(currentExportConfig.replace());
exportItems.add(currentExportItem);
}
currentExportItem = null;
currentExportConfig = null;
}
// 建立新的工作薄。
SXSSFWorkbook wb = POIUtil.newSXSSFWorkbook();
double sheetNo = Math.ceil(data.size() / mMaxSheetRecords);// 取出一共有多少個sheet.
// =====多sheet生成填充資料=====
for (int index = 0; index <= (sheetNo == 0.0 ? sheetNo : sheetNo - 1); index++) {
SXSSFSheet sheet = POIUtil.newSXSSFSheet(wb, sheetName + (index == 0 ? "" : "_" + index));
// 建立表頭
SXSSFRow headerRow = POIUtil.newSXSSFRow(sheet, 0);
for (int i = 0; i < exportItems.size(); i++) {
SXSSFCell cell = POIUtil.newSXSSFCell(headerRow, i);
POIUtil.setColumnWidth(sheet, i, exportItems.get(i).getWidth(), exportItems.get(i).getDisplay());
cell.setCellValue(exportItems.get(i).getDisplay());
CellStyle style = handler.headCellStyle(wb);
if (style != null) {
cell.setCellStyle(style);
}
}
// 產生資料行
if (data != null && data.size() > 0) {
int startNo = index * mMaxSheetRecords;
int endNo = Math.min(startNo + mMaxSheetRecords, data.size());
for (int i = startNo; i < endNo; i++) {
SXSSFRow bodyRow = POIUtil.newSXSSFRow(sheet, i + 1 - startNo);
for (int j = 0; j < exportItems.size(); j++) {
// 處理單元格值
String cellValue = exportItems.get(j).getReplace();
if ("".equals(cellValue)) {
try {
cellValue = BeanUtils.getProperty(data.get(i), exportItems.get(j).getField());
} catch (Exception e) {
log.error("獲取" + exportItems.get(j).getField() + "的值失敗.", e);
}
}
// 格式化單元格值
if (!"".equals(exportItems.get(j).getConvert())) {
cellValue = convertCellValue(Integer.parseInt(cellValue), exportItems.get(j).getConvert());
}
// 單元格寬度
POIUtil.setColumnWidth(sheet, j, exportItems.get(j).getWidth(), cellValue);
SXSSFCell cell = POIUtil.newSXSSFCell(bodyRow, j);
if (cellValue == null) {
continue;
}
// fix: 當值為“”時,當前index的cell會失效
try {
cell.setCellValue("".equals(cellValue) ? null : cellValue);
} catch (Exception e) {
// e.printStackTrace();
}
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setColor(exportItems.get(j).getColor());
style.setFont(font);
// style.setWrapText(true);
cell.setCellStyle(style);
}
}
}
}
try {
// 生成Excel檔案並下載.(通過response物件是否為空來判定是使用瀏覽器下載還是直接寫入到output中)
POIUtil.writeByLocalOrBrowser(mResponse, handler.exportFileName(sheetName), wb, out);
} catch (Exception e) {
log.error("生成Excel檔案失敗:" + e.getMessage(), e);
return false;
}
log.info(String.format("Excel處理完成,共生成資料:%s行 (不包含表頭),耗時:%s seconds.", (data != null ? data.size() : 0),
(System.currentTimeMillis() - begin) / 1000F));
return true;
}
}
(4)在需要匯出的地方,只需要一行程式碼就可以實現
ExcelKit.$Export(this.entityClass, response).toExcel(List<資料集合>, 匯出檔名);
2 匯入功能會根據表頭的文字和註解中的文字進行匹配,然後根據註解中的匯入順序,實現匯入
(1)首先還是實體
@ExcelConfig(value = "商品名稱",colIndex = 1)
private String goods_name; //商品的名稱
這裡的colIndex就是匯入的順序
(2)然後定義匯入的方法
/**
* 匯入操作
* @param clazz
* @param xls
* @return
* @throws Exception
*/
public static List importExcel(Class<?> clazz, InputStream xls) throws Exception{
try {
// 取得Excel
// HSSFWorkbook wb = new HSSFWorkbook(xls);
// HSSFSheet sheet = wb.getSheetAt(0);
Workbook wb = WorkbookFactory.create(xls);
Sheet sheet = wb.getSheetAt(0);
Field[] fields = clazz.getDeclaredFields();
List<Field> fieldList = new ArrayList<Field>(fields.length);
for (Field field : fields) {
if (field.isAnnotationPresent(ExcelConfig.class)) {
ExcelConfig modelProp = field.getAnnotation(ExcelConfig.class);
if (modelProp.colIndex() != -1) {
fieldList.add(field);
}
}
}
// 行迴圈
List<AbstractPageEntity> modelList = new ArrayList<AbstractPageEntity>(sheet.getPhysicalNumberOfRows() * 2);
for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
// 資料模型
AbstractPageEntity model = (AbstractPageEntity) clazz.newInstance();
int nullCount = 0;
Exception nullError = null;
for (Field field : fieldList) {
ExcelConfig modelProp = field.getAnnotation(ExcelConfig.class);
// HSSFCell cell = sheet.getRow(i).getCell(modelProp.colIndex());
Cell cell = sheet.getRow(i).getCell(modelProp.colIndex()-1);
try {
if (cell == null || cell.toString().length() == 0) {
nullCount++;
if (!modelProp.nullable()) {
nullError = new Exception(StringUtil.format(notnullerror,
new String[]{"" + (1 + i), modelProp.value(), modelProp.value()}));
}
} else if (field.getType().equals(Date.class)) {
if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(), WpDate.strToLongDate(String.valueOf(cell).trim()));
} else {
BeanUtils.setProperty(model, field.getName(), new Date(WpDate.strToLongDate(String.valueOf(cell).trim()).getTime()));
}
} else if (field.getType().equals(Timestamp.class)) {
if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(),
new Timestamp(WpDate.strToLongDate(String.valueOf(cell).trim()).getTime()));
} else {
BeanUtils.setProperty(model, field.getName(),
new Timestamp(cell.getDateCellValue().getTime()));
}
} else if (field.getType().equals(java.sql.Date.class)) {
if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(),
new java.sql.Date(WpDate.strToLongDate(String.valueOf(cell).trim()).getTime()));
} else {
BeanUtils.setProperty(model, field.getName(),
new java.sql.Date(cell.getDateCellValue().getTime()));
}
} else if (field.getType().equals(java.lang.Integer.class)) {
if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(), (int) cell.getNumericCellValue());
} else if (Cell.CELL_TYPE_STRING == cell.getCellType()&&modelProp.formatTime()==0) {
BeanUtils.setProperty(model, field.getName(), Integer.parseInt(String.valueOf(cell).trim()));
} else if (Cell.CELL_TYPE_STRING == cell.getCellType()&&modelProp.formatTime()==1) {
BeanUtils.setProperty(model, field.getName(), WpDate.dateTostamp(WpDate.strToLongDate(String.valueOf(cell).trim())));
}
} else if (field.getType().equals(java.math.BigDecimal.class)) {
if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(),
new BigDecimal(cell.getNumericCellValue()));
} else if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(), new BigDecimal(String.valueOf(cell).trim()));
}
} else {
if (Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(),
new BigDecimal(cell.getNumericCellValue()));
} else if (Cell.CELL_TYPE_STRING == cell.getCellType()) {
BeanUtils.setProperty(model, field.getName(), String.valueOf(cell).trim());
}
}
} catch (Exception e) {
e.printStackTrace();
throw new Exception(StringUtil.format(errormsg, new String[]{"" + (1 + i), modelProp.value()})
+ "," + e.getMessage());
}
}
if (nullCount == fieldList.size()) {
break;
}
if (nullError != null) {
throw nullError;
}
modelList.add(model);
}
return modelList;
}catch (OpenException e){
e.printStackTrace();
}finally{
xls.close();
}
return null;
}
這裡利用到了反射的機制。
(2)然後在需要匯入的地方,用一行程式碼實現
ExcelKit.importExcel(entityClass, file.getInputStream());