导出工具类

package com.wanshun.common.utils;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;

import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

import com.wanshun.common.utils.StringUtil;
/**
 * cvs下载公共方法
 * @author wg
 *
 */
public class CsvUtils {
	/**CSV文件列分隔符*/
	private static final String CSV_COLUMN_SEPARATOR=",";
	
	/** CSV文件列分割父*/
	private static final String CSV_RN="\r\n";
	
	
	private static Logger logger = LoggerFactory.getLogger(CsvUtils.class);
	
	/**
	 * 数据初始化
	 * @param data 数据库查出来的数据
	 * @param displayColNames csv表头
	 * @param matchColNames  data中的key ,可以说是数据库字段了,原本为”0001”类型的数据在excel中打开会被默认改变为”1”的数据。 解决方法 :key前加"'"用于特殊处理;
	 * @param 例如 输入列名为"num"数字为 001,则传入的key值为"-num",保证输出为字符串
	 * @return
	 */
	
	public static String formatCsvData(List<Map<String, Object>> data,
			String displayColNames, String matchColNames) {

		StringBuffer buf = new StringBuffer();

		String[] displayColNamesArr = null;
		String[] matchColNamesMapArr = null;

		displayColNamesArr = displayColNames.split(",");
		matchColNamesMapArr = matchColNames.split(",");

		// 输出列头
		for (int i = 0; i < displayColNamesArr.length; i++) {
			buf.append(displayColNamesArr[i]).append(CSV_COLUMN_SEPARATOR);
		}
		buf.append(CSV_RN);

		if (null != data) {
			// 输出数据
			for (int i = 0; i < data.size(); i++) {

				for (int j = 0; j < matchColNamesMapArr.length; j++) {
					//处理list<Map>中 value=null的数据
					Object object = data.get(i).get(matchColNamesMapArr[j]);
					if(object==null){
						object = data.get(i).get(matchColNamesMapArr[j].substring(1));
					}
					if(object==null){
						buf.append(CSV_COLUMN_SEPARATOR);	

					} else {
						String str = object.toString();
						if (StringUtil.isEcho(str, "^[0-9]+\\.[0-9]{1,2}$")) {//如果是金额类型不做处理
							buf.append(str).append(CSV_COLUMN_SEPARATOR);
						}else{
							if (str.contains(",")) {
								str = str.replace(",", ",");
							}
							buf.append("\t").append(str).append(CSV_COLUMN_SEPARATOR);
						}
					}
				}
				buf.append(CSV_RN);
			}
		}
		logger.debug("csv file Initialize successfully>>>>>version1.1");
		return buf.toString();
	}
	
	  public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {    
	        if (map == null)  
	            return null;  
	  
	        Object obj = beanClass.newInstance();  
	  
	        org.apache.commons.beanutils.BeanUtils.populate(obj, map);  
	  
	        return obj;  
	    }    
	      
	    public static Map<?, ?> objectToMap(Object obj) {  
	        if(obj == null)  
	            return null;   
	  
	        return new org.apache.commons.beanutils.BeanMap(obj);  
	    }    
	      
	/**
	 * 导出
	 * @param fileName 文件名
	 * @param content 内容
	 * @param request
	 * @param response
	 * @throws IOException
	 */
 	public static void exportCsv(String fileName, String content,HttpServletRequest request, 
			HttpServletResponse response) throws IOException {

		// 读取字符编码
		String csvEncoding = "UTF-8";

		// 设置响应
		response.setCharacterEncoding(csvEncoding);
		response.setContentType("text/csv; charset=" + csvEncoding); //定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
        //以下三个set,都是为了避免做了禁止浏览器缓存的操作,否则在ie导出数据的时候出问题,但是在firefox和opera等就可以导出
		response.setHeader("Pragma", "public");  
		response.setHeader("Cache-Control", "max-age=30");//客户机可以接收生存期不大于30s的响应
		response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");//让客户端可以访问除上述Header之外的首部信息,Content-Disposition字段放到Access-Control-Expose-Header里

		final String userAgent = request.getHeader("USER-AGENT");
		if(StringUtils.contains(userAgent, "MSIE")){//IE浏览器
			 fileName = URLEncoder.encode(fileName,"UTF-8");
        }else if(StringUtils.contains(userAgent, "Mozilla")){//google,火狐浏览器
        	 fileName = new String(fileName.getBytes(), "UTF-8");
        }else{
        	 fileName = URLEncoder.encode(fileName,"UTF-8");//其他浏览器
        }
		response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");  //作为文件下载的一个标识段,来确保浏览器弹出下载对话框,有些内容如.txt,.jpg文件会直接在浏览器中显示,如果要提示用户是否下载,就要使用Content-Disposition字段,一定要加上attachment值,Content-Disposition属性有两个值,一个是inline,默认在浏览器中直接显示文件内容,一个是attachment,弹出对话框让用户下载
		 
		// 写出响应,OutPutStream对文件输出
		OutputStream os = response.getOutputStream();
		os.write(new   byte []{( byte ) 0xEF ,( byte ) 0xBB ,( byte ) 0xBF }); //用来检测字节流是否是utf-8编码的,因为一些文件传输的要求,UCS规范要求在传输文件时先传输字符"ZERO WIDTH NO-BREAK SPACE"来表名字节流的顺序,但是UTF-8不需要BOM来 表明字节顺序,但可以用BOM来 表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF,所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了
		os.write(content.getBytes("UTF8"));
		logger.info("csv file download completed");
	}
 	
}

变成要导出的数据格式工具类

package com.wanshun.common.filesystem.csv;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;

public class CsvFileUtil {
	private static final Logger logger = Logger.getLogger(CsvFileUtil.class);
	
	/**CSV文件列分隔符*/
	private static final String CSV_COLUMN_SEPARATOR=",";
	
	/** CSV文件列分割符【换行和回车符】*/
	private static final String CSV_RN="\r\n";
	
	
	public static <T extends CSVFormat> byte[] create(String[] header, List<T> data) {

		StringBuffer buf = new StringBuffer();

		//拼接头部
		for (int i = 0; i < header.length; i++) {
			buf.append(header[i]).append(CSV_COLUMN_SEPARATOR);
		}
		buf.append(CSV_RN);

		//拼接内容
		if (null != data && !data.isEmpty()) {
			for (CSVFormat csvFormat : data) {
				buf.append(csvFormat.formatToString()).append(CSV_RN);
			}
		}

		return buf.toString().getBytes();

	}

	public static <T extends CSVFormat> String createStr(String[] header, List<T> data) {

		StringBuffer buf = new StringBuffer();

		//拼接头部
		for (int i = 0; i < header.length; i++) {
			buf.append(header[i]).append(CSV_COLUMN_SEPARATOR);  //每一列标题后添加,
		}

		buf.append(CSV_RN);//标题作为一行数据,添加换行符【列1,列2,列3】+添加换行符

		//拼接内容
		if (null != data && !data.isEmpty()) {
			for (CSVFormat csvFormat : data) {
                //把每一个list列数据转换成String加,再加换行符list1【列1,列2,列3】+换行符+下一行数据
				buf.append(csvFormat.formatToString()).append(CSV_RN);
			}
		}

		return buf.toString();  

	}
	
	/**
	 * csv转map
	 * @param bytes
	 * @param keys
	 * @return
	 */
	public static List<Map<String, Object>> importExcel(byte[] bytes,String[] keys){
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes), Charset.forName("utf8")));  
		 String line; 
		 String[] array;
		 int mark = 0;
		 int size =0;
		 Map<String, Object> map;
		 try {
			while ( (line = br.readLine()) != null ) {  
			     if(!line.trim().equals("")&& mark!=0){
			    	 array = line.split(",");
			    	 map = new HashMap<String, Object>();
			    	 size = array.length>=keys.length ? keys.length : array.length;
			    	 for(int i = 0 ; i < size ; i ++) {
			    		 map.put(keys[i], array[i]);
			    	 }
			    	 if (!map.isEmpty()) {
			    		 list.add(map);
			    	 }
			     }   
			     mark++;
			 }
		} catch (IOException e) {
			logger.info("csv转Map<String,Object>异常:" + e);
		} 
		return list;
	}
 
}

controller.java

package com.wanshun.controller.member;

import com.wanshun.member.dto.output.OfflineUserInfoDto;
import com.wanshun.member.dto.output.OfflineUserInfoExportOutDto;
import com.wanshun.member.dto.output.OfflineUserInfoOutDto;
import com.wanshun.member.rpcapi.RpcOfflineUserService;
import com.wanshun.service.SysAccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
  /**
     * 线下用户信息导出
     * @param offlineUserInfoExportDto
     * @return
     */
    @OpRequestSelectMapping(op = "exportOfflineUserList", desc = "线下用户信息导出")
    public void exportOfflineUserList(@RequestBody OfflineUserInfoExportDto offlineUserInfoExportDto, HttpServletRequest request, HttpServletResponse response){
        logger.info("线下用户信息导出入参:{}", JsonUtil.toJson(offlineUserInfoExportDto));

        List<OfflineUserInfoExportOutDto> offlineUserInfoExportOutDtos = new ArrayList<>();
        //从数据库中查询所需要导出的数据
        List<OfflineUserInfoDto> offlineUserList = rpcOfflineUserService.exportOfflineUserList(offlineUserInfoExportDto);

        offlineUserList.stream().forEach((offlineUserInfoExportDto1)->{
            if(offlineUserInfoExportDto1 != null){
                OfflineUserInfoExportOutDto offlineUserInfoExportOutDto = new OfflineUserInfoExportOutDto();
                BeanUtils.copyProperties(offlineUserInfoExportDto1,offlineUserInfoExportOutDto);
                offlineUserInfoExportOutDtos.add(offlineUserInfoExportOutDto);
            }
        });

用于导出的数据的实体类型===》OfflineUserInfoExportOutDto.class

package com.wanshun.member.dto.output;

import com.google.common.collect.Lists;
import com.wanshun.common.filesystem.csv.CSVFormat;
import com.wanshun.common.utils.DateUtil;
import lombok.Data;

import java.util.Date;
import java.util.List;
import java.util.Optional;


@Data
public class OfflineUserInfoExportOutDto implements CSVFormat {

    //导出文件的横标题(String[] )
    public static final String[] HEADEER = {
            "用户姓名",
            "用户手机号",
            "车牌号",
            "拨打热线时间",
            "投保交强险",
            "投保商业险"
    };

    /**
     * 用户姓名
     */
    private String userName;

    /**
     * 用户手机号
     */
    private String userPhone;

    /**
     * 车牌号
     */
    private String licenseNumber;

    /**
     * 拨打热线时间
     */
    private String callPhoneTime;

    /**
     * 投保商业险 否,是
     */
    private String isApplyBiz;

    /**
     * 投保交强险 否,是
     */
    private String isApplyForce;


    @Override
    public String formatToString() {
        List<Object> list = Lists.newArrayList();

        list.add(userName);
        list.add(userPhone);
        list.add(licenseNumber);
        list.add(callPhoneTime);
        list.add(isApplyBiz);
        list.add(isApplyForce);

        //StringBuffer线程安全,且
        StringBuilder sb = new StringBuilder();
        for (Object item : list) {
            //如果导出的字段是Date类型的,进行格式转换
            if (null != item && item instanceof Date) {
                item = DateUtil.getDateStr_YYYY_MM_DD_HH_MM_SS_FORMAT((Date) item);
            }
            //item字段不为null时,添加缩进字符,添加逗号字符
            sb.append(Optional.ofNullable(item).orElse("")).append("\t").append(",");
        }
        return sb.toString();//转换成String字符串,有逗号
    }
}

CSVFormat.java---->一个统一的导出csv文件的接口【只是规定了一个统一的数据导出的类型方法】

package com.wanshun.common.filesystem.csv;


public interface CSVFormat {
	String formatToString();
}

Postman测试

用python解析postman导出的json文件 postman 导出_CSV

   Send and Download后文件的名称可能是乱码的,这个我还不知道为什么,但是在正常浏览器中下载的话不是乱码的,下载完之后直接用Excel或wps打开即可

用python解析postman导出的json文件 postman 导出_List_02

用python解析postman导出的json文件 postman 导出_List_03

我们导出.csv文件的CsvFileUtil 数据格式转换的工具类只用到了String createStr()方法,因为真正导出.csv文件的工具类中只有String类型接收要导出的数据,导入数据还没试,过几天来还愿,把导入数据试一下,但愿能试