Java POI实现excel大数据量下载
Posted claindoc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java POI实现excel大数据量下载相关的知识,希望对你有一定的参考价值。
最近,在做一个数据导出的功能,需求描述如下:
当用户在页面点击“搜索”,查询符合条件的记录,然后点击导出,实现excel文件下载,直接输出到浏览器,保存文件到本地。
需求分析
- 满足需求基本功能,考虑性能问题,对下载记录数可以控制。
- 大文件导出之前,进行压缩,用户导出文件压缩包之后,使用本地的解压工具可以解压。
以下是其代码实现记录,防止后续重复工作。
参考链接:http://poi.apache.org/components/spreadsheet/index.html
- 抽象excel文件内容
- 下载excel文件
- 压缩文件
- 直接输出到浏览器
POI实现Excel文件下载的接口为org.apache.poi.ss.usermodel.Workbook
,它有三个实现,如下图:
查阅官网,比较三者性能(性能对比图如下),最后决定选取org.apache.poi.xssf.streaming.SXSSFWorkbook
.
import java.util.List;
import java.util.Map;
/**
* excel表格信息
*
* @author xiaoqiang.guo.wb
*
*/
public class ExcelShellProperty {
/**
* excel表头属性值
*/
private String[] colProperties;
/**
* excel表格展示信息
*/
private String[] colView;
/**
* excel sheet名称
*/
private String sheetName;
/**
* excel 数据
*/
private List<Map<String,Object>> dataList;
/**
* excel 附加信息 如总记录数等
*/
private String extMsg;
public String[] getColProperties() {
return colProperties;
}
public void setColProperties(String[] colProperties) {
this.colProperties = colProperties;
}
public String[] getColView() {
return colView;
}
public void setColView(String[] colView) {
this.colView = colView;
}
public String getSheetName() {
return sheetName;
}
public void setSheetName(String sheetName) {
this.sheetName = sheetName;
}
public List<Map<String,Object>> getDataList() {
return dataList;
}
public void setDataList(List<Map<String,Object>> dataList) {
this.dataList = dataList;
}
public String getExtMsg() {
return extMsg;
}
public void setExtMsg(String extMsg) {
this.extMsg = extMsg;
}
}
public class ExcelUtils2Xlsx {
private static final Logger logger = LoggerFactory.getLogger(ExcelUtils2Xlsx.class);
public File create(List<ExcelShellProperty> sheets,HttpServletResponse response, File fileDir,int n) {
logger.info("@@ExcelUtils2Xlsx create excel文件 start");
Long start = System.currentTimeMillis();
SXSSFWorkbook book = null;
File tempfile = null;
FileOutputStream out = null;
try {
book = new SXSSFWorkbook(100);//keep 100 rows in memory, exceeding rows will be flushed to disk
int sheetNo = 1;
for (ExcelShellProperty excelShellProperty : sheets) {
Sheet sheet = book.createSheet(excelShellProperty.getSheetName());
//add
Font headFont = book.createFont();
headFont.setFontName("宋体");
headFont.setColor(IndexedColors.BLUE.index);
headFont.setBoldweight((short)12);
//样式设置
CellStyle cellStyle = book.createCellStyle();
cellStyle.setFont(headFont);
cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
cellStyle.setFillBackgroundColor(IndexedColors.YELLOW.index);
String[] colView = excelShellProperty.getColView();
int i = 0;
//生成表头信息
Row row = sheet.createRow(0);
for (int len = colView.length; i < len; ++i) {
sheet.setColumnWidth(i, 3766);
Cell cell = row.createCell(i);
cell.setCellValue(colView[i]);
cell.setCellStyle(cellStyle);
}
setExcelBody(excelShellProperty.getDataList(),excelShellProperty.getColProperties(),excelShellProperty.getExtMsg(),colView.length, sheet,headFont,cellStyle);
++sheetNo;
}
// 将流信息转换为文件流 创建文件流
String tempFileName = createTempFileName(n);
tempfile = new File(fileDir + "//" + tempFileName);
logger.info("@@ExcelUtils2Xlsx 文件路径" + tempfile.getAbsolutePath()+ ";文件名称为" + tempfile.getName());
out = new FileOutputStream(tempfile);
book.write(out);
out.flush();
book.dispose();
Long end = System.currentTimeMillis();
logger.info("@@ExcelUtils2Xlsx create excel文件 end,生成excel文件用时 "+(end-start)+"毫秒");
return tempfile;
} catch (Exception e) {
logger.info("@@ExcelUtils2Xlsx occur exception",e);
} finally {
try {
if(out != null){
IOUtils.closeQuietly(out);
}
} catch (Exception e2) {
logger.error("@@ExcelUtils2Xlsx downLoad occur exception",e2);
}
}
return null;
}
/**
* 生成excel文件名称
*
* @param i
* @return
*/
private String createTempFileName(int i) {
String tempFileName = "指令查询结果"+i+".xlsx";
return tempFileName;
}
/**
* 表格内容设置 注意需要分页信息
*
* @param massList
* @param colProperties
* @param extMsg
* @param label
* @param sheet1
* @throws WriteException
*/
public static void setExcelBody(List<Map<String,Object>> massList, String[] colProperties,String extMsg,int length,Sheet sheet, Font bodyFont,CellStyle cellStyle )throws Exception {
//设置字体信息
bodyFont.setFontName("宋体");
bodyFont.setBoldweight((short)10);
bodyFont.setColor(IndexedColors.BLACK.index);
//设置单元格格式
cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
int i = 0;
for (int len = massList.size(); i < len; ++i) {
Row row = sheet.createRow(i+1);
Map<String,Object> listOrderMap = massList.get(i);
int j = 0;
for (int lenth2 = colProperties.length;j < lenth2; ++j) {
Cell cell = row.createCell(j);
cell.setCellStyle(cellStyle);
String cellV = listOrderMap.get(colProperties[j]) == null ? "": (String) listOrderMap.get(colProperties[j]);
cell.setCellValue(cellV);
}
}
//额外信息
if(StringUtils.isNotEmpty(extMsg)){
int lastRowNum = sheet.getLastRowNum();
Row row = sheet.createRow(lastRowNum+1);
Cell createCell = row.createCell(0);
createCell.setCellStyle(cellStyle);
createCell.setCellValue(extMsg);
}
}
}
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 查询导出处理类
*
* @author xiaoqiang.guo.wb
*
*/
public class ExcelInfoDownloadUtils {
private static final Logger logger = LoggerFactory.getLogger(ExcelInfoDownloadUtils.class);
/**
* 表头展示信息
*/
private static final String[] ExcelInfoViews = {"批次号","交易号","商户编号","商户名称","业务类型","反馈信息",
"付款银行账号","付款账户名称", "收款银行账号","收款账户名称","币种", "金额","渠道名称", "指令状态","创建日期","修改日期","指令发送时间"};
/**
* 表头展示信息对应属性
*/
private static final String[] ExcelInfoProperties = {"batchIdStr","orderSeqIdStr","memberCodeStr","memberName","isIndvidualStr","bankRemind",
"payerAcctNo","payerAcctName","payeeAcctNo","payeeAcctName","payeeCurrencyStr","amountStr","channelName","statusStr","crtTimeStr","updTimeStr","sendTimeStr"};
/**
* 生成excel文件
*
* @param dataList
* @param response
* @param fileDir
* @param i
* @param extMsg
* @return
*/
public File createFile(List<Map<String,Object>> dataList,HttpServletResponse response,File fileDir,int i,String extMsg){
logger.info("@@BankInstrInfoDownloadHelper 下载指令查询结果list size"+dataList.size());
//1.设置表格输出元信息
List<ExcelShellProperty> downList = new ArrayList<ExcelShellProperty>();
ExcelShellProperty excelShellProperty = new ExcelShellProperty();
excelShellProperty.setColProperties(BankInstrInfoProperties);
excelShellProperty.setColView(BankInstrInfoViews);
excelShellProperty.setDataList(dataList);
excelShellProperty.setSheetName("指令查询");
excelShellProperty.setExtMsg(extMsg);
downList.add(excelShellProperty);
ExcelUtils2Xlsx excelHelper = new ExcelUtils2Xlsx();
File file = excelHelper.create(downList,response,fileDir,i);
return file;
}
}
实现导出数据之前,需要对导出数据的格式进行预处理,并将其转换为map格式。
以下是实现Zip压缩的实现:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* zip文件处理工具类
*
* @author xiaoqiang.guo.wb
*
*/
public class ZipUtils {
private static final Log log = LogFactory.getLog(ZipUtils.class);
/**
* 压缩文件
*
* @param srcfile
* File[] 需要压缩的文件列表
* @param zipfile
* File 压缩后的文件
*/
public static void zipFiles(List<File> srcfile, File zipfile) {
ZipOutputStream out = null;
try {
out = new ZipOutputStream(new FileOutputStream(zipfile));
for (int i = 0; i < srcfile.size(); i++) {
File file = srcfile.get(i);
FileInputStream in = new FileInputStream(file);
out.putNextEntry(new ZipEntry(file.getName()));
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.closeEntry();
IOUtils.closeQuietly(in);
}
// Complete the ZIP file
//out.close();
} catch (IOException e) {
log.error("@@ZipUtils zipFiles exception:" , e);
}finally{
if(out != null){
IOUtils.closeQuietly(out);
}
}
}
/**
* 解压缩
*
* @param zipfile
* File 需要解压缩的文件
* @param descDir
* String 解压后的目标目录
*/
public static void unZipFiles(File zipfile, String descDir) {
InputStream in = null ;
OutputStream out = null;
try {
// Open the ZIP file
ZipFile zf = new ZipFile(zipfile);
for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {
// Get the entry name
ZipEntry entry = ((ZipEntry) entries.nextElement());
String zipEntryName = entry.getName();
in = zf.getInputStream(entry);
out = new FileOutputStream(descDir + zipEntryName);
byte[] buf1 = new byte[1024];
int len;
while ((len = in.read(buf1)) > 0) {
out.write(buf1, 0, len);
}
// Close the file and stream
out.flush();
//in.close();
//out.close();
}
} catch (IOException e) {
log.error("ZipUtils unZipFiles exception:",e);
}finally{
if(in != null){
IOUtils.closeQuietly(in);
}
if(out != null){
IOUtils.closeQuietly(out);
}
}
}
public static void downFile(HttpServletResponse response,
String serverPath, String fileName) {
InputStream ins = null;
BufferedInputStream bins = null;
OutputStream outs = null;
BufferedOutputStream bouts = null;
try {
String path = serverPath + "//"+fileName;
File file = new File(path);
if (file.exists()) {
ins = new FileInputStream(path);
bins = new BufferedInputStream(ins);// 放到缓冲流里面
outs = response.getOutputStream();// 获取文件输出IO流
bouts = new BufferedOutputStream(outs);
response.setContentType("application/x-download");// 设置response内容的类型
response.setHeader("Content-disposition","attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));// 设置头部信息
int bytesRead = 0;
byte[] buffer = new byte[8192];
// 开始向网络传输文件流
while ((bytesRead = bins.read(buffer, 0, 8192)) != -1) {
bouts.write(buffer, 0, bytesRead);
}
bouts.flush();// 这里一定要调用flush()方法 将缓存中的数据写刷新到目标源
//ins.close();
//bins.close();
//outs.close();
//bouts.close();
} else {
log.info("zip文件输出时,未找到相关文件位置信息");
}
} catch (IOException e) {
log.info("@@ZipUtils downFile error" ,e);
} finally {
if(ins != null){
IOUtils.closeQuietly(ins);
}
if(bins != null){
IOUtils.closeQuietly(bins);
}
if(outs != null){
IOUtils.closeQuietly(outs);
}
if(bouts != null){
IOUtils.closeQuietly(bouts);
}
}
}
/**
* 递归删除文件夹
*
* @param path
* @return
*/
public static boolean delete(String path) {
File file = new File(path);
boolean success = false;
if (file.isDirectory()) {
String[] list = file.list(); // 返回该目录下的所有文件的文件名称(注意,只显示直属目录下的信息)
for (int i = 0; i < list.length; i++) {
success = delete(path + "//"+ list[i]); // 注意该处的分隔符号
}
} else {
success = file.delete();
}
return success;
}
}
最后,附上bean转map的实现。
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
/**
* bean to map 转换工具类
* @author xiaoqiang.guo.wb
*
*/
public class BeanUtils {
/**
* 空字符串
*/
static final String emptyString = "";
private final static Logger log = LoggerFactory.getLogger(BeanUtils.class);
/**
* Map中根据key获得字符串
*
* @param params 容器
* @param key key值
* @return 字符串类型的value
*/
public static <K, V> String getString(Map<K, V> params, K key)
{
if (CollectionUtils.isEmpty(params))
{
return emptyString;
}
V value = params.get(key);
return null == value ? emptyString : value.toString();
}
/**
* bean转 Map
* @param bean
* @return Map
*/
public static Map<String, Object> bean2Map(Object javaBean) {
Map<String, Object> map = new HashMap<String, Object>();
try {
// 获取javaBean属性
BeanInfo beanInfo = Introspector.getBeanInfo(javaBean.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo
.getPropertyDescriptors();
if (propertyDescriptors != null && propertyDescriptors.length > 0) {
String propertyName = null; // javaBean属性名
Object propertyValue = null; // javaBean属性值
for (PropertyDescriptor pd : propertyDescriptors) {
propertyName = pd.getName();
if (!propertyName.equals("class")) {
Method readMethod = pd.getReadMethod();
propertyValue = readMethod.invoke(javaBean,
new Object[0]);
map.put(propertyName, propertyValue);
}
}
}
} catch (Exception e) {
log.error("bean to map error");
}
return map;
}
}
(完)
以上是关于Java POI实现excel大数据量下载的主要内容,如果未能解决你的问题,请参考以下文章
怎么使用java Poi解决导入excel表格大数据量时的内存溢出问