版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012557538/article/details/82382661
一:注解部分
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表格,复杂的合并神马的,请自行实现,本文方法并不适用