Spring Boot下的一种导出Excel文件的代码框架
1、前言
在Spring Boot项目中,将数据导出成Excel格式文件是常见的功能。与Excel文件导入类似,此处也用代码框架式的方式实现Excel文件导出,使得代码具有可重用性,并保持导出数据转换的灵活性。
相对于导入Excel文件的处理,导出Excel文件要简单一些。这里的Excel文件支持xlsx格式。
2、基本框架
包括一个接口类ExcelExportable和一个Excel导出处理类ExcelExportHandler,以及支持ExcelExportable接口类的实体类。与基类相比,使用接口类的好处是可以实现多个接口类,而不会有多重继承的麻烦。这样实体类可以同时支持Excel和CSV格式文件的导出。
2.1、Excel导出处理类ExcelExportHandler
ExcelExportHandler类的代码如下:
package com.abc.questInvest.excel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.List;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import com.abc.questInvest.excel.ExcelExportable;
/**
* @className : ExcelExportHandler
* @description : Excel导出处理类
*
*/
public class ExcelExportHandler<T extends ExcelExportable> {
/**
*
* @methodName : exportExcelFile
* @description : 导出Excel文件供下载
* @param rowDataList : 导出的数据列表
* @param excelFilePath : Excel文件临时文件路径
* @param sheetName: sheet页名称
* @throws Exception : 发生异常时,抛出
*
*/
public void exportExcelFile(List<T> rowDataList,String excelFilePath,String sheetName)
throws Exception {
if (rowDataList.size() == 0) {
//必须要有导出数据,否则创建标题列失败
throw new Exception("无导出数据.");
}
XSSFWorkbook wb = null;
wb = new XSSFWorkbook();
//创建sheet页
XSSFSheet sheet = wb.createSheet(sheetName);
//创建标题行
createTitle(wb,sheet,rowDataList.get(0));
//添加数据
addSheetContent(sheet,wb,rowDataList);
//输出文件数据供下载
outputExcelFile(excelFilePath,wb);
}
/**
*
* @methodName : createTitle
* @description : 设置标题行
* @param workbook : workbook对象
* @param sheet : sheet对象
* @throws Exception : 发生异常时,抛出
*
*/
private void createTitle(XSSFWorkbook workbook, XSSFSheet sheet,T rowData) throws Exception{
XSSFRow row = sheet.createRow(0);
//取得标题行
String[] arrTitles = rowData.outputTitleList();
//设置列宽
for (int i = 0; i < arrTitles.length; i++){
//设置固定宽度,setColumnWidth的第二个参数的单位是1/256个字节宽度
sheet.setColumnWidth(i,arrTitles[i].getBytes("UTF-8").length * 256);
//设置自适应宽度,性能不高,故不用了
//sheet.autoSizeColumn(i);
}
//设置为居中加粗
XSSFCellStyle style = workbook.createCellStyle();
XSSFFont font = workbook.createFont();
font.setBold(true);
style.setAlignment(HorizontalAlignment.CENTER);
style.setFont(font);
XSSFCell cell;
for (int i = 0; i < arrTitles.length; i++){
cell = row.createCell(i);
cell.setCellValue(arrTitles[i]);
cell.setCellStyle(style);
}
}
/**
*
* @methodName : addSheetContent
* @description : 为sheet对象添加数据行内容
* @param sheet : sheet对象
* @param workbook : workbook对象
* @param rowDataList : 数据行列表
* @throws Exception : 发生异常时,抛出
*
*/
private void addSheetContent(XSSFSheet sheet, XSSFWorkbook workbook, List<T> rowDataList)
throws Exception
{
//单元格居中
XSSFCellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
//数据行下标从1开始
int rowNum=1;
//遍历导出数据行
for(int i=0;i<rowDataList.size();i++){
XSSFRow row = sheet.createRow(rowNum);
XSSFCell cell;
T rowData = rowDataList.get(i);
String[] arrRow = rowData.outputList();
for (int j = 0; j < arrRow.length; j++) {
cell = row.createCell(j);
cell.setCellValue(arrRow[j]);
cell.setCellStyle(style);
}
rowNum++;
}
}
/**
*
* @methodName : outputExcelFile
* @description : 输出Excel文件
* @param filePath : 输出的文件路径
* @param workbook : workbook对象
* @throws Exception : 发生异常时,抛出
*
*/
private void outputExcelFile(String filePath,XSSFWorkbook workbook) throws Exception{
OutputStream outputStream = new FileOutputStream(new File(filePath));
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
}
}
ExcelExportHandler支持泛型T,T限定必需支持ExcelExportable接口类。只要实体类实现ExcelExportable类的接口方法,就可以利用ExcelExportHandler的方法实现Excel文件导出。
2.2、可Excel导出的接口类ExcelExportable
ExcelExportable类的代码如下:
package com.abc.questInvest.excel;
/**
* @className : ExcelExportable
* @description : 可Excel导出的接口类,由POJO类实现
*
*/
public interface ExcelExportable {
/**
*
* @methodName : outputTitleList
* @description : 输出标题列表,用于标题行
* @return : 字符串数组
*
*/
public String[] outputTitleList();
/**
*
* @methodName : outputList
* @description : 输出数据列表,用于数据行
* @return : 字符串数组
*
*/
public String[] outputList();
}
ExcelExportable类定义了2个接口方法:
- outputTitleList方法,输出标题列表,用于标题行。
- outputList方法,输出数据列表,用于数据行。
2.3、实体类例子
对需要导出Excel文件的现有的实体类进行改造,使之支持ExcelExportable接口类。
实体类为AnswerInfo,代码如下:
package com.abc.questInvest.entity;
import java.util.Date;
import javax.persistence.Column;
import com.abc.questInvest.excel.ExcelExportable;
import lombok.Data;
/**
* @className : AnswerInfo
* @description : 答卷信息类,支持Excel数据导出
*
*/
@Data
public class AnswerInfo implements ExcelExportable {
// 记录id
@Column(name = "rec_id")
private Integer recId;
// 发布任务id
@Column(name = "task_id")
private Integer taskId;
// 问卷id
@Column(name = "questionnaire_id")
private Integer questionnaireId;
// 问题编号
@Column(name = "question_no")
private Integer questionNo;
// 答案
@Column(name = "answer")
private String answer;
//========记录操作信息================
// 操作人姓名
@Column(name = "login_name")
private String loginName;
// 记录删除标记,保留
@Column(name = "delete_flag")
private Byte deleteFlag;
// 创建时间
@Column(name = "create_time")
private Date createTime;
// 更新时间
@Column(name = "update_time")
private Date updateTime;
//========实现ExcelExportable接口================
//导出的Excel数据的列数
private static final int COLUMN_COUNT = 5;
/**
*
* @methodName : outputTitleList
* @description : 输出标题列表,用于标题行
* @return : 字符串数组
*
*/
@Override
public String[] outputTitleList() {
String[] arrTitle = new String[COLUMN_COUNT];
arrTitle[0] = "问卷ID";
arrTitle[1] = "发布任务ID";
arrTitle[2] = "记录ID";
arrTitle[3] = "题号";
arrTitle[4] = "答案";
return arrTitle;
}
/**
*
* @methodName : outputList
* @description : 输出数据列表,用于数据行
* @return : 字符串数组
*
*/
@Override
public String[] outputList() {
String[] arrRowData = new String[COLUMN_COUNT];
//各属性字段,从数据库中取出,都为非null值
//此处实现导出字段的物理含义转换和计算
arrRowData[0] = questionnaireId.toString();
arrRowData[1] = taskId.toString();
arrRowData[2] = recId.toString();
arrRowData[3] = questionNo.toString();
arrRowData[4] = answer;
return arrRowData;
}
}
2.4、单元测试
下面进行Excel文件导出的单元测试。测试代码如下:
package com.abc.questInvest.excel;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.abc.questInvest.entity.AnswerInfo;
/**
* @className : ExcelExportHandlerTest
* @description : Excel文件导出测试
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ExcelExportHandlerTest {
@Test
public void exportExcelFileTest() {
ExcelExportHandler<AnswerInfo> excelExp = new ExcelExportHandler<AnswerInfo>();
AnswerInfo item = new AnswerInfo();
item.setQuestionnaireId(1);
item.setTaskId(1);
item.setRecId(1);
item.setQuestionNo(1);
item.setAnswer("A");
dataList.add(item);
item = new AnswerInfo();
item.setQuestionnaireId(1);
item.setTaskId(1);
item.setRecId(1);
item.setQuestionNo(2);
item.setAnswer("B");
dataList.add(item);
item = new AnswerInfo();
item.setQuestionnaireId(1);
item.setTaskId(1);
item.setRecId(1);
item.setQuestionNo(3);
item.setAnswer("A");
dataList.add(item);
item = new AnswerInfo();
item.setQuestionnaireId(1);
item.setTaskId(1);
item.setRecId(2);
item.setQuestionNo(1);
item.setAnswer("B");
dataList.add(item);
item = new AnswerInfo();
item.setQuestionnaireId(1);
item.setTaskId(1);
item.setRecId(2);
item.setQuestionNo(2);
item.setAnswer("B");
dataList.add(item);
item = new AnswerInfo();
item.setQuestionnaireId(1);
item.setTaskId(1);
item.setRecId(2);
item.setQuestionNo(3);
item.setAnswer("C");
dataList.add(item);
String property = System.getProperty("user.dir");
String filePath = property + "\\answer_data_Q1_T1.xlsx";
String sheetName = "Q1_T1";
List<AnswerInfo> dataList = new ArrayList<AnswerInfo>();
try {
excelExp.exportExcelFile(dataList,filePath, sheetName);
}catch(Exception e) {
e.printStackTrace();
}
}
}
执行测试代码,可以看到导出的Excel文件,文件内容如下:
3、Excel文件导出并下载
在导出生成了Excel文件后,只需与文件下载代码结合起来,就可以实现Excel文件下载了。
文件下载,可作为静态的公共方法,放入工具类中。代码如下:
/**
*
* @methodName : download
* @description : 下载指定路径的文件
* @param response : reponse对象
* @param filePath : 需要下载的文件路径
* @param contentType : response header中的ContentType,常用取值如下:
* 普通二进制文件 : application/octet-stream
* Excel文件 : application/vnd.ms-excel
* 文本文件 : text/plain; charset=utf-8
* html文件 : text/html; charset=utf-8
* xml文件 : text/xml; charset=utf-8
* jpeg文件 : image/jpeg
* @throws Exception : 异常发生时,抛出。没有异常,说明下载成功。
*
*/
public static void download(HttpServletResponse response,String filePath,
String contentType) throws Exception{
File file = new File(filePath);
if (file.exists()) {
//设置读取流的缓存为1K
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
//设置ContentType
response.setContentType(contentType);
// 下载文件能正常显示中文
String filename = filePath.substring(filePath.lastIndexOf("/")+1);
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
//获取输入流
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
//输出流
OutputStream os = response.getOutputStream();
int len = bis.read(buffer);
while (len != -1) {
os.write(buffer, 0, len);
len = bis.read(buffer);
}
//关闭流
if (bis != null) {
bis.close();
}
if (fis != null) {
fis.close();
}
}
}
调用download方法,contentType参数取值为“application/vnd.ms-excel”即可。