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

    作者:阿拉伯1999
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    养成良好习惯,好文章随手顶一下。
  • 相关阅读:
    《C++ Primer》读书笔记之第15章:面向对象编程
    Color Space: HSI
    Color Space: Lab
    C# 特殊关键字
    WPF 打开文件、文件夹
    EmguCV+Win7+Visual C# 2012 配置
    C# 常用结构
    C#抽象类、抽象方法、虚方法
    C# Image 、 byte[] 、Bitmap之间的转化
    java反射机制基础
  • 原文地址:https://www.cnblogs.com/alabo1999/p/14904684.html
Copyright © 2020-2023  润新知