• Spring Boot下的一种导出CSV文件的代码框架


    1、前言

    ​ CSV,逗号分隔值(Comma-Separated Values),即为逗号分隔的文本文件。如果值中含有逗号、换行符、制表符(Tab)、单引号及双引号,则需要用双引号括起来;如果值中包含双引号,则需要用两个双引号来替换。

    ​ CSV大量用于不同系统之间的数据交换,是一种非常常用的文件格式。

    ​ 在Spring Boot项目中,将数据导出成CSV格式文件是常见的功能。与Excel文件导出类似,此处也用代码框架式的方式实现CSV文件导出,使得代码具有可重用性,并保持导出数据转换的灵活性。

    2、基本框架

    ​ 基本框架包括一个接口类CsvExportable和一个CSV文件导出处理类CsvExportHandler,以及支持CsvExportable接口类的实体类。与基类相比,使用接口类的好处是实体类可以实现多个接口类,而没有多重继承的问题。

    2.1、CSV文件导出处理类CsvExportHandler

    ​ CsvExportHandler类的代码如下:

    package com.abc.questInvest.csv;
    
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.util.List;
    
    /**
     * @className	: CsvExportHandler
     * @description	: CSV文件导出处理类
     *
     */
    public class CsvExportHandler<T extends CsvExportable> {
    
    	/**
    	 * 
    	 * @methodName		: exportCsvFile
    	 * @description		: 导出CSV文件
    	 * @param rowDataList	: T类型对象列表
    	 * @param csvFilePath	: 输出的CSV文件路径
    	 * @throws Exception	: 异常发生时,抛出
    	 *
    	 */
    	public void exportCsvFile(List<T> rowDataList,String csvFilePath) 
    			throws Exception{
    		
    		if (rowDataList.size() == 0) {
    			//必须要有导出数据,否则创建标题列失败
    			throw new Exception("无导出数据.");
    		}	
    		
    		//取得第一个对象
    		T rowDataObj = rowDataList.get(0);
    		
    		//将数据写入csv格式文件
    		writeToCsv(rowDataList,rowDataObj,csvFilePath);
    	}
    	
    	/**
    	 * 
    	 * @methodName		: writeToCsv
    	 * @description		: 将数据写入csv格式文件
    	 * @param dataList	: T类型对象列表
    	 * @param rowDataObj: T类型对象
    	 * @param filePath	: 输出的文件路径
    	 * @throws Exception	: 异常发生时,抛出
    	 *
    	 */
        private void writeToCsv(List<T> dataList, T rowDataObj, String filePath) throws Exception {
            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            String enter = "
    ";
    		String sLine;
            StringBuffer write ;
        	fos = new FileOutputStream(new File(filePath));
        	bos = new BufferedOutputStream(fos);
    		//标题行
    		sLine = rowDataObj.outputCsvTitleLine();
    		write = new StringBuffer();
    		write.append(sLine);
    		//加换行符
    		write.append(enter);
    		bos.write(write.toString().getBytes("UTF-8"));
            for (int i = 0; i < dataList.size(); i++) {
                write = new StringBuffer();
                T rowData = dataList.get(i);
    			//输出CSV格式的数据行
    			sLine = rowData.outputCsvDataLine();
    			//写数据行
    			write.append(sLine);
                //加换行符
                write.append(enter);
                bos.write(write.toString().getBytes("UTF-8"));
            }
            //刷新数据
            bos.flush();
            
            //关闭流
            bos.close();
            fos.close();
        }	
    }
    

    ​ CsvExportHandler支持泛型T,T限定必需支持CsvExportable接口类。只要实体类实现CsvExportable类的接口方法,就可以利用CsvExportHandler的方法实现CSV文件导出。

    2.2、可CSV导出的接口类CsvExportable

    ​ CsvExportable类的代码如下:

    package com.abc.questInvest.csv;
    
    /**
     * @className	: CsvExportable
     * @description	: CSV导出对象接口类
     *
     */
    public interface CsvExportable {
    	/**
    	 * 
    	 * @methodName		: outputTitleLine
    	 * @description		: 输出标题行字符串
    	 * @return			: 标题行字符串
    	 *
    	 */
    	public String outputCsvTitleLine();
    	
    	/**
    	 * 
    	 * @methodName		: outputDataLine
    	 * @description		: 输出数据行
    	 * @return			: 符合CSV格式的数据行字符串
    	 *
    	 */
    	public String outputCsvDataLine();
    	
    	/**
    	 * 
    	 * @methodName		: CSVFormat
    	 * @description		: 将输入字符串格式化成CSV格式的字符串
    	 * @param input		: 输入字符串
    	 * @return			: 符合CSV格式的字符串
    	 *
    	 */
    	public static String CSVFormat(String input) {
    		boolean bFound = false;
    		//如果值中含有逗号、换行符、制表符(Tab)、单引号,双引号,则需要用双引号括起来;
    		//如果值中包含双引号,则需要用两个双引号来替换。
    		//正则匹配:",'"
    	"
    		bFound = input.matches("(.*)(,|'|"|
    |
    |	)(.*)");
    		if (bFound) {
    			//如果存在匹配字符
    			//先将双引号替换为两个双引号
    			String sTemp = input.replaceAll(""", """");
    			//然后,两端使用"字符
    			sTemp =""" + sTemp + """;
    			return sTemp;
    		}
    		return input;
    	}	
    }
    

    ​ CsvExportable类定义了2个接口方法:

    • outputCsvTitleLine方法,输出标题行字符串。

    • outputCsvDataLine方法,输出数据行字符串。

    ​ CsvExportable类还提供了一个静态方法:

    • CSVFormat方法,将字符串格式化成CSV格式的字符串。针对包含特殊字符的输入字符串进行特别处理。

    2.3、实体类例子

    ​ 对需要导出CSV文件的现有的实体类进行改造,使之支持CsvExportable接口类。

    ​ 实体类为AnswerInfo,代码如下:

    package com.abc.questInvest.entity;
    
    import java.util.Date;
    
    import javax.persistence.Column;
    
    import com.abc.questInvest.csv.CsvExportable;
    import com.abc.questInvest.excel.ExcelExportable;
    
    import lombok.Data;
    
    /**
     * @className	: AnswerInfo
     * @description	: 答卷信息类,支持CSV格式数据导出
     *
     */
    @Data
    public class AnswerInfo implements CsvExportable {
        // 记录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;  
            
    	//========实现CsvExportable接口================
    	/**
    	 * 
    	 * @methodName		: outputTitleLine
    	 * @description		: 输出标题行字符串
    	 * @return			: 标题行字符串
    	 *
    	 */
        @Override
    	public String outputCsvTitleLine() {
        	String title = "问卷ID,发布任务ID,记录ID,题号,答案";
        	return title;
        }
    	
    	/**
    	 * 
    	 * @methodName		: outputDataLine
    	 * @description		: 输出数据行
    	 * @return			: 符合CSV格式的数据行字符串
    	 *
    	 */
        @Override
    	public String outputCsvDataLine() {
        	String dataLine = "";
        	//如果预计字符串可能包含双引号,则需要调用CSVFormat方法处理
        	dataLine = "" + questionnaireId + ","
        			+ taskId + "," 
        			+ recId + "," 
        			+ questionNo + ","    	
        			+ CsvExportable.CSVFormat(answer);
        	return dataLine;
        }
    }
    

    2.4、单元测试

    ​ 下面进行CSV文件导出的单元测试。测试代码如下:

    package com.abc.questInvest.csv;
    
    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;
    import com.abc.questInvest.excel.ExcelExportHandler;
    
    /**
     * @className	: CsvExportHandlerTest
     * @description	: CSV文件导出测试
     *
     */
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class CsvExportHandlerTest {
    	@Test
    	public void exportCsvFileTest() {
    		CsvExportHandler<AnswerInfo> csvExport = new CsvExportHandler<AnswerInfo>();
    		List<AnswerInfo> dataList = new ArrayList<AnswerInfo>();
    		
    		AnswerInfo item = new AnswerInfo();
    		item.setQuestionnaireId(1);
    		item.setTaskId(1);
    		item.setRecId(1);
    		item.setQuestionNo(1);
    		item.setAnswer("'A,B,C',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,B,c',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);
    		
    		//有tab符号
    		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(""abc",C");
    		dataList.add(item);
    		
    		String property = System.getProperty("user.dir");
    		String filePath = property + "\answer_data_Q1_T1.csv";
    		
    		try {
    			csvExport.exportCsvFile(dataList,filePath);			
    		}catch(Exception e) {
    			e.printStackTrace();
    		}		
    	}
    }
    

    ​ 执行测试,结果生成了answer_data_Q1_T1.csv文件,用文本编辑器打开,内容如下:

    问卷ID,发布任务ID,记录ID,题号,答案
    1,1,1,1,"'A,B,C',A"
    1,1,1,2,B
    1,1,1,3,"'A,B,c',A"
    1,1,2,1,",B,"
    1,1,2,2,"	B"
    1,1,2,3,"""abc"",C"
    

    ​ 可见导出的是CSV格式的数据。

    作者:阿拉伯1999
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    养成良好习惯,好文章随手顶一下。
  • 相关阅读:
    oldboy_09_03day_test1
    oldboy_09_03day
    java消息队列
    es6语法([...arr], set/map数据结构,数组扩展,箭头函数等)
    Angular 2 Expression Changed After It Has Been Checked Exception
    jQuery之Deferred对象详解
    js面向对象:Object类,静态属性,闭包,私有属性, call和apply的使用,继承的三种实现方法
    Angular 4.x 动态创建组件
    JS中this的四种用法
    typescript主键自增长
  • 原文地址:https://www.cnblogs.com/alabo1999/p/14904731.html
Copyright © 2020-2023  润新知