1. 程式人生 > >JAVA反射方式實現簡易通用EXCEL下載

JAVA反射方式實現簡易通用EXCEL下載

一:註解部分

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表格,複雜的合併神馬的,請自行實現,本文方法並不適用