最近在做Excel文件導出的功能,花了一個小時研究了一下這個功能,並應用在了項目中,在小數據量導出方面符合項目需求,花個時間整理一下,方便以後使用及遺忘。
1. 首先是jar的引用,次示例通過maven引入所需要的jar包
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</exclusion>
</exclusions>
</dependency>
2.定義一個類似於Map的key-value對象,用來存儲導出的表頭信息。
package yang.zheng.util.excel2;
public class Pro {
private String name;
private String desc;
public Pro(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
3.定義一個excel配置類,用來配置excel的名稱、表頭存儲集合。
package yang.zheng.util.excel2;
import java.util.ArrayList;
import java.util.List;
public class ExcelConfig {
private String tempDir;
/**
* 標題第一行數據1
*/
private String titleName1;
/**
* sheetName1
*/
private String sheetName1;
/**
* 文件名稱
*/
private String fileName;
/**
* 要顯示的數據 sheetName1
*/
private List<Pro> pros1;
public String getTempDir() {
return tempDir;
}
public void setTempDir(String tempDir) {
this.tempDir = tempDir;
}
public String getTitleName1() {
return titleName1;
}
public void setTitleName1(String titleName1) {
this.titleName1 = titleName1;
}
public String getSheetName1() {
return sheetName1;
}
public void setSheetName1(String sheetName1) {
this.sheetName1 = sheetName1;
}
public List<Pro> getPros1() {
return pros1;
}
public void addPro1(Pro pro1){
if(pros1==null){
pros1 = new ArrayList<Pro>();
}
pros1.add(pro1);
}
public void addPro1(String name ,String desc){
if(pros1==null){
pros1 = new ArrayList<Pro>();
}
Pro pro1 = new Pro(name, desc);
pros1.add(pro1);
}
public void setPros1(List<Pro> pros1) {
this.pros1 = pros1;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
4.抽象出一個Excel導出的模板類,定義公共信息和方法。
package yang.zheng.util.excel2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import yang.zheng.util.common.BeanUtil;
public abstract class ExcelTemplate<E> {
public File createExcel(String modelpath) {
List<E> data1 = getData1();
if (data1.size() > 50000) {
throw new RuntimeException("數據大於50000行,請縮小查詢參數", null);
}
ExcelConfig config = getConfig();
File file = buildExcelFileNew(data1, config, modelpath);
return file;
}
/**
* 生成Excel數據
* @param data1
* @param config
* @param modelpath
* @return
*/
private File buildExcelFileNew(List<E> data1, ExcelConfig config, String modelpath) {
XSSFWorkbook workbook = null;
try {
// 創建 Excel 文件的輸入流對象
FileInputStream excelFileInputStream = new FileInputStream(modelpath);
// XSSFWorkbook 就代表一個 Excel 文件
// 創建其對象,就打開這個 Excel 文件
workbook = new XSSFWorkbook(excelFileInputStream);
// 輸入流使用後,及時關閉!這是文件流操作中極好的一個習慣!
excelFileInputStream.close();
XSSFSheet sheet1 = workbook.getSheetAt(0);
List<Pro> pros1 = config.getPros1();
// 從第三行開始,顯示數據
for (int i = 0; i < data1.size(); i++) {
E t = data1.get(i);
XSSFRow rowdata = sheet1.createRow(i + 1);
buildDataRowNewT(rowdata, pros1, t);
}
File tempDir = new File(config.getTempDir());
if (!tempDir.exists()) {
tempDir.mkdirs();
}
String fileName = config.getFileName();
File f = new File(tempDir + "/" + System.currentTimeMillis() + "_" + fileName);
// 將excel 寫入到文件中,
FileOutputStream fos = new FileOutputStream(f);
workbook.write(fos);
fos.close();
// 若是需要壓縮
if (isNeedZip()) {
f = compressFile(f);
return f;
}
return f;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (workbook != null) {
try {
// wb.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 壓縮文件
*
* @param
* @return
*/
protected File compressFile(File file) {
System.err.println("壓縮暫時沒有實現");
return file;
}
/**
* 顯示數據
*
* @param rowdata
* @param pros
* @param t
*/
private void buildDataRowNewT(XSSFRow rowdata, List<Pro> pros, E t) {
for (int j = 0; j < pros.size(); j++) {
try {
Object o = BeanUtil.forceGetProperty(t, pros.get(j).getName());
Cell cell = rowdata.createCell(j);
if (o == null) {
cell.setCellValue("");
} else {
if (o instanceof Date) {
cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd").format((Date) o ));
} else if (o instanceof BigDecimal) {
BigDecimal a = (BigDecimal) o;
cell.setCellValue(String.valueOf(a));
} else {
cell.setCellValue(String.valueOf(o));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 獲取數據
*
* @return
*/
protected abstract List<E> getData1();
/**
* 獲取數據的顯示配置
*
* @return
*/
protected abstract ExcelConfig getConfig();
/**
* 是否需要壓縮
*
* @return
*/
protected boolean isNeedZip() {
return false;
}
}
5.新建一個實體類Student,用於Excel導出數據的映射實體。
package yang.zheng.util.excel2;
public class Student{
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
6.新建一個子類繼承該模板類,通過實現抽象方法,定義自己的業務邏輯。package yang.zheng.util.excel2;
import java.util.List;
public class SupExcelTemplate extends ExcelTemplate<Student>{
private List<Student> data1;
public SupExcelTemplate(List<Student> data) {
this.data1 = data;
}
@Override
protected List<Student> getData1() {
return data1;
}
@Override
protected ExcelConfig getConfig() {
ExcelConfig excelConfig = new ExcelConfig();
excelConfig.setFileName("數據導出.xlsx");
excelConfig.setTempDir("D:/");
excelConfig.addPro1("name", "姓名");
excelConfig.addPro1("age", "年齡");
return excelConfig;
}
}
7.工具類BeanUtil用於表頭與數據之間的映射。
package yang.zheng.util.common;
import java.lang.reflect.Field;
public class BeanUtil {
/**
* 暴力獲取對象變量值,忽略private,protected修飾符的限制.
*
* @throws NoSuchFieldException
* 如果沒有該Field時拋出.
*/
public static Object forceGetProperty(Object object, String propertyName)
throws NoSuchFieldException {
Field field = getDeclaredField(object, propertyName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
Object result = null;
try {
result = field.get(object);
} catch (IllegalAccessException e) {
}
field.setAccessible(accessible);
return result;
}
public static Field getDeclaredField(Object object, String propertyName)
throws NoSuchFieldException {
return getDeclaredField(object.getClass(), propertyName);
}
/**
* 循環向上轉型,獲取對象的DeclaredField.
*
* @throws NoSuchFieldException
* 如果沒有該Field時拋出.
*/
@SuppressWarnings("unchecked")
public static Field getDeclaredField(Class clazz, String propertyName)
throws NoSuchFieldException {
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getDeclaredField(propertyName);
} catch (NoSuchFieldException e) {
// Field不在當前類定義,繼續向上轉型
//將異常捕獲繼續向父類查找傳入屬性,直到找到object跳出循環
}
}
throw new NoSuchFieldException("No such field: " + clazz.getName()
+ '.' + propertyName);
}
}
8.新建一個測試類,測試導出功能。package yang.zheng.util;
import java.util.ArrayList;
import java.util.List;
import yang.zheng.util.excel2.ExcelTemplate;
import yang.zheng.util.excel2.Student;
import yang.zheng.util.excel2.SupExcelTemplate;
public class TestExcel {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>();
list.add(new Student("小明", 12));
list.add(new Student("王是", 14));
ExcelTemplate excel = new SupExcelTemplate(list);
excel.createExcel("D:/123.xlsx");
}
}