Java POI實現excel大數據量下載
阿新 • • 發佈:2018-10-25
spec member 數據量 system pac 空字符 ger ima bin
,它有三個實現,如下圖:
最近,在做一個數據導出的功能,需求描述如下:
當用戶在頁面點擊“搜索”,查詢符合條件的記錄,然後點擊導出,實現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大數據量下載