导出工具类
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测试
Send and Download后文件的名称可能是乱码的,这个我还不知道为什么,但是在正常浏览器中下载的话不是乱码的,下载完之后直接用Excel或wps打开即可
我们导出.csv文件的CsvFileUtil 数据格式转换的工具类只用到了String createStr()方法,因为真正导出.csv文件的工具类中只有String类型接收要导出的数据,导入数据还没试,过几天来还愿,把导入数据试一下,但愿能试