JAVA反射方式實現簡易通用EXCEL下載
阿新 • • 發佈:2019-02-12
一:註解部分
package com.jianlejun.common.msoffice.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 用於繫結實體屬性、Excel列名、Excel列索引之間的關係 * @author allan */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface ColField { public String colName() ;//列名 public String position();//列標 }
二:實體部分
package com.jianlejun.common.msoffice.test; import java.util.Date; import com.jianlejun.common.msoffice.annotation.ColField; public class User { @ColField(colName = "使用者名稱", position = "0") private String userName; @ColField(colName = "年齡", position = "1") private int age; @ColField(colName = "地址", position = "3") private String address; @ColField(colName = "生日", position = "2") private Date birthDay; private String weight; public User(String userName, int age, Date birthday, String address) { this.userName = userName; this.age = age; this.birthDay = birthday; this.address = address; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public String getWeight() { return weight; } public void setWeight(String weight) { this.weight = weight; } }
三:抽象類
package com.jianlejun.common.msoffice.excel; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; public abstract class AbstractExcel { private Workbook workbook; private Sheet sheet; private static final String DEFAULT_SHEET_NAME = "DEFAULT"; public abstract <T> Workbook createExcel(List<T> rowData) throws Exception; public abstract void buildTitle();// 標題非必須 public abstract void buildHeader();// 表頭非必須 public abstract <T> void buildContent(List<T> rowData, Class<T> clazz) throws Exception;// 內容必須 public abstract void buildTail();// 尾部非必須 public Workbook buildWorkbook() { workbook = new HSSFWorkbook(); return workbook; } public Sheet buildSheet() { int i = workbook.getNumberOfSheets(); sheet = workbook.createSheet(DEFAULT_SHEET_NAME + "_" + (i + 1)); return sheet; } public Sheet buildSheet(String sheetName) { int i = workbook.getNumberOfSheets(); if (sheetName != null && !sheetName.isEmpty()) { sheet = workbook.createSheet(sheetName); } else { sheet = workbook.createSheet(DEFAULT_SHEET_NAME + "_" + (i + 1)); } return sheet; } public Workbook initExcel() { this.buildWorkbook(); this.buildSheet(); return workbook; } public Workbook initExcel(String sheetName) { this.buildWorkbook(); this.buildSheet(sheetName); return workbook; } }
四:實現類
package com.jianlejun.common.msoffice.excel;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jianlejun.common.msoffice.annotation.ColField;
/**
* @description 簡易報表操作類
* @author allan
* @param <T>
*
*/
public class EasyExcelOp extends AbstractExcel {
private Logger log = LoggerFactory.getLogger(EasyExcelOp.class);
private Workbook workbook;
private Sheet sheet;
private boolean isZeroRowUsed = false;// 採用組合思想,需特殊考慮下標為零的行
private Map<String, Map<String, String>> colMap = new HashMap<String, Map<String, String>>();
public EasyExcelOp() {
workbook = initExcel();
}
public static EasyExcelOp getInstance() {
return new EasyExcelOp();
}
@Override
public <T> Workbook createExcel(List<T> rowData) throws Exception {
sheet = workbook.getSheetAt(0);// 指明使用哪張sheet表
Class clazz = rowData.get(0).getClass();
colMap = this.getExcColRelation(clazz);
this.buildTitle();
this.buildHeader();
this.buildContent(rowData, clazz);
this.buildTail();
return workbook;
}
/**
* 獲取Excel列名、列key,列索引之間的對映關係
*
* @param <T>
*/
private <T> Map<String, Map<String, String>> getExcColRelation(Class<T> clazz) throws Exception {
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
if (!f.isAnnotationPresent(ColField.class)) {
continue;
//throw new Exception(f.getName() + ": is not annotated by annotation");
}
ColField ColFieldAnnoation = f.getDeclaredAnnotation(ColField.class);
String position = ColFieldAnnoation.position();
String colName = ColFieldAnnoation.colName();
if (position == null || position.isEmpty()) {
throw new Exception("position is required!");
}
if (colName == null) {
throw new Exception("colName is required!");
}
if (!position.matches("[0-9]+")) {
throw new Exception("can't convert position to Integer");
}
Map<String, String> m = new HashMap<String, String>();
m.put(f.getName(), colName);
colMap.put(position, m);
}
return colMap;
}
@Override
public void buildHeader() {
int begin = this.getRowTotal();
Row row = sheet.createRow(begin);
for (Entry<String, Map<String, String>> entry : colMap.entrySet()) {
Cell cell = row.createCell(Integer.parseInt(entry.getKey()));
for (String colName : entry.getValue().values()) {
// 必定只有一個元素
// TODO 是否可以不用迴圈方式來獲取值,待優化
cell.setCellValue(colName);
break;
}
}
}
private int getRowTotal() {
if (isZeroRowUsed) {
return sheet.getLastRowNum() + 1;// 獲取sheet最後一行的行號(索引從零開始)
} else {
isZeroRowUsed = true;
return sheet.getLastRowNum();
}
}
// 應用反射根據成員變數呼叫對應的setter/getter
public <T> Object invoke(Object clazz, String fieldName)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException {
PropertyDescriptor pd = new PropertyDescriptor(fieldName, clazz.getClass());
// 從屬性描述器中獲取 get 方法
Method method = pd.getReadMethod();
Object obj = method.invoke(clazz);
return obj == null ? "" : obj;
}
/**
* 構造EXCEL表格的主要內容體
*/
@Override
public <T> void buildContent(List<T> rowData, Class<T> clazz) throws Exception {
if (rowData == null || rowData.isEmpty()) {
return;
}
int begin = this.getRowTotal();// 獲取內容行從第幾行開始填充
for (int i = 0; i < rowData.size(); i++) {
Row row = sheet.createRow(begin + i);
for (int j = 0; j < colMap.size(); j++) {
Cell cell = row.createCell(j);// TODO 單元格型別未設定,預設字串
for (String colNameVariable : colMap.get(String.valueOf(j)).keySet()) {
cell.setCellValue(this.invoke(rowData.get(i), colNameVariable).toString());
}
}
}
}
public Workbook getWorkbook() {
return workbook;
}
public void setWorkbook(Workbook workbook) {
this.workbook = workbook;
}
@Override
public void buildTitle() {
// TODO Auto-generated method stub
}
@Override
public void buildTail() {
// TODO Auto-generated method stub
}
}
五:控制器輸出文件
@RequestMapping("/exportExc")
public void exportExcel(@RequestBody Teacher teacher, HttpServletResponse resp) throws Exception {
List<User> rowData = new ArrayList<User>//這裡經過業務處理得到列表資料
// 輸出
Workbook workbook = EasyExcelOp.getInstance().createExcel(rowData);
outputExcel(workbook, resp);
}
public void outputExcel(Workbook workbook, HttpServletResponse resp) throws IOException {
String fileName = "xxxx記錄_" + System.currentTimeMillis() + ".xls";
OutputStream ops = resp.getOutputStream();
resp.setContentType("application/octet-stream;charset=UTF-8");
resp.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
workbook.write(ops);
ops.flush();
ops.close();
}
六:結束語
用傳統的POI方式下載Excel文件,列變動,增加列,刪除列,變動列位置等,都會引起程式的大修改,此方法只需要關注標識excel列的實體類即可,但此方式有一種缺陷,僅僅適用於簡易的excel表格,複雜的合併神馬的,請自行實現,本文方法並不適用