在项目开发中,我们经常会遇到需要从数据库导出大量数据的情况,经常会因为数据量太大而难以导出。
下面介绍下我是怎么处理的:
1、用CSV代替POI导出,因为CSV格式比Eexcel同样数据量的情况下节省很多存储空间
2、分配导出,每次查询5万或者10万条,生成一个CSV文件,CSVUtils工具栏
3、多文件打包,用ZipOutputStream将多个文件打包zip压缩包
4、打包完后,再用GZIP工具类压缩输出,减少传输流量
5、设置响应头,告诉浏览器当前gzip压缩格式,浏览器会自动解压
具体示例如下:
String[] titles = new String[]{"订单号","邮箱","国家","省份","城市","街道地址","邮编", "电话","姓名"}; // 导出列标题
String[] titleTags = new String[]{"ordernumber","email","country","province","city","address","postalcode", "telephone","name"}; // 对应bean中的字段名称
public void orderListExport(@RequestBody OrderQueryCondition condition, HttpServletResponse response) throws Exception{
try (ByteArrayOutputStream result = new ByteArrayOutputStream();
OutputStream outputStream = response.getOutputStream();
ZipOutputStream zos = new ZipOutputStream(result)){
// 每次查询最大10万
condition.setLimit(OrderConstants.ORDER_EXPORT_QUERY_LIMIT);
// 查询
List<OrderQueryVO> list = null;
for (int i = 0; i == 0 || list.size() == OrderConstants.ORDER_EXPORT_QUERY_LIMIT; i++) {
// 最多只能导100万
if(i>9){
break;
}
// 导出CSV
// 设置起始行
condition.setOffset(OrderConstants.ORDER_EXPORT_QUERY_LIMIT * i);
list = orderQueryService.orderList(condition);
// 文件打包
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
CSVUtils.writeCSVFile(titles, titleTags, list, out);
zos.putNextEntry(new ZipEntry(i + ".csv"));
zos.write(out.toByteArray());
}
}
zos.close();
// 压缩传输
byte[] compress = GZIPUtils.compress(result.toByteArray());
String fileName = "order-list("+DateFormatUtils.format(new Date(), "yyyyMMddHHmmss")+").zip";
// 设定输出文件头
response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName).getBytes("GBK"), "ISO8859-1"));
// 告诉浏览器当前gzip压缩格式,浏览器会自动解压
response.setHeader("content-encoding", "gzip");
outputStream.write(compress);
} catch (Exception e){
logger.error("导出订单异常:{}", JSON.toJSONString(condition), e);
} finally {
System.gc(); // 内存回收,避免OOM
}
}