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”即可。

posted @ 2021-06-19 19:43  阿拉伯1999  阅读(285)  评论(0编辑  收藏  举报