CSV相较于xlsx在导出大数据量+字段多的场景下速度更快!
本地测试导出30万条数据,CSV比xlsx快20秒+。不过当数据量小或字段少(2个字段)这种情况下使用CSV并没有明显的优势,并且由于CSV存储相同数据占用的磁盘大小比xlsx的大得多(差不多一倍),在网络传输较为频繁的场景也不适合使用CSV。
以下为动态导出CSV的功能代码,表头不固定。
pom
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.5</version>
</dependency>
code
/**
* @param fieldHeaderMap 表头 LinkedHashMap类型,保障表头顺序
* @param response
*/
public void exportCSV(Map<String,String> fieldHeaderMap, HttpServletResponse response) {
log.info("开始导出Excel");
String fileName = "XXX.csv";
OutputStream outputStream = null;
CSVPrinter csvPrinter = null;
OutputStreamWriter osw = null;
int currentCount = 0;
try {
response.reset();
//虽然是CSV文件,但是可以使用Excel打开,一般也是使用Excel打开,因此设置内容格式为Excel
response.setHeader("Content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
outputStream = response.getOutputStream();
osw = new OutputStreamWriter(outputStream, "utf-8");
//解决第一行以ID开头文件打开有报错问题
osw.write(new String(new byte[] { (byte) 0xEF, (byte) 0xBB,(byte) 0xBF }));
String[] headerArr = new String[fieldHeaderMap.size()];
String[] keyArr = new String[fieldHeaderMap.size()];
fieldHeaderMap.keySet().toArray(keyArr);//字段名称
fieldHeaderMap.values().toArray(headerArr);//字段中文名称
//写入表头
CSVFormat csvFormat = CSVFormat.EXCEL.withHeader(headerArr);
csvPrinter = new CSVPrinter(osw, csvFormat);
。。。省略业务判断代码
Map<String,Object> temp = new LinkedHashMap<>();
//可设置最大导出条数
while (currentCount < TaskParam.MAX_DETAIL_LIMIT && currentPage <= totalPage){
List<Map<String, Object>> records = 。。。分页查询数据
if(CollectionUtils.isEmpty(records)){
break;
}
currentCount += records.size();
//需要保证输出的表头与数据对应
for (Map<String, Object> record : records) {
for (String s : keyArr) {
temp.put(s, record.get(s));
}
csvPrinter.printRecord(temp.values());
temp.clear();
}
csvPrinter.flush();
//还可以设置超时时间
}
} catch (Exception e) {
log.error("export error ", e);
} finally {
if(csvPrinter != null){
try {
csvPrinter.close();
} catch (IOException e) {
log.error("关闭csvPrinter异常", e);
}
}
if(osw != null){
try {
osw.close();
} catch (IOException e) {
log.error("关闭OutputStreamWriter异常", e);
}
}
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
log.error("关闭outputStream异常", e);
}
}
}
log.info("写入{}条数据", currentCount);
}
其中CSV导出的代码没多少,关键的就初始化一个printer,然后调用printRecord将数据写入。
需要注意这句:
osw.write(new String(new byte[] { (byte) 0xEF, (byte) 0xBB,(byte) 0xBF }));
如果不加这句,若第一行数据以ID开头(很多表的主键是叫ID,导出的数据的表头也就是以ID开头了),则导出的CSV打开会报错。