POI導出工具類
工作中常常會遇到一些圖表需要導出的功能,在這里自己寫了一個工具類方便以后使用(使用POI實現)。
項目依賴
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.6</version>
</dependency>
package com.adcc.eoss.util; import org.apache.poi.hssf.usermodel.*; import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.io.UnsupportedEncodingException; /** * Created by LMQ on 2019/3/18. */
public class ExcelUtil { public static HSSFWorkbook getHSSFWorkbook(String sheetName, String[] title, String[][] values) { return getHSSFWorkbook(sheetName, title, values, null); } /** * 導出Excel * * @param sheetName sheet名稱 * @param title 標題 * @param values 內容 * @param wb HSSFWorkbook對象 * @return
*/
public static HSSFWorkbook getHSSFWorkbook(String sheetName, String[] title, String[][] values, HSSFWorkbook wb) { // 第一步,創建一個HSSFWorkbook,對應一個Excel文件
if (wb == null) { wb = new HSSFWorkbook(); } // 第二步,在workbook中添加一個sheet,對應Excel文件中的sheet
HSSFSheet sheet = wb.createSheet(sheetName); // 第三步,在sheet中添加表頭第0行,注意老版本poi對Excel的行數列數有限制
HSSFRow row = sheet.createRow(0); // 第四步,創建單元格,並設置值表頭 設置表頭居中
HSSFCellStyle style = wb.createCellStyle(); style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 創建一個居中格式
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 創建一個垂直居中格式 //聲明列對象
HSSFCell cell = null; //創建標題
for (int i = 0; i < title.length; i++) { cell = row.createCell(i); cell.getCellStyle().setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); cell.getCellStyle().setAlignment(HSSFCellStyle.ALIGN_CENTER); cell.setCellValue(title[i]); cell.setCellStyle(style); } //創建內容
for (int i = 0; i < values.length; i++) { row = sheet.createRow(i + 1); for (int j = 0; j < values[i].length; j++) { //將內容按順序賦給對應的列對象
row.createCell(j).setCellValue(values[i][j]); } } return wb; } //響應到客戶端
public static void returnClient(HttpServletResponse response, String fileName, HSSFWorkbook wb) { try { setResponseHeader(response, fileName); OutputStream os = response.getOutputStream(); wb.write(os); os.flush(); os.close(); } catch (Exception e) { e.printStackTrace(); } } //發送響應流方法
public static void setResponseHeader(HttpServletResponse response, String fileName) { try { try { fileName = new String(fileName.getBytes(), "ISO8859-1"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block
e.printStackTrace(); } response.setContentType("application/octet-stream;charset=ISO8859-1"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); response.addHeader("Pargam", "no-cache"); response.addHeader("Cache-Control", "no-cache"); } catch (Exception ex) { ex.printStackTrace(); } } }
通過這個工具類我們只需要傳遞對應參數就能夠實現簡單的EXCEL導出功能。
下面是一個導出的例子
/** * 導出用戶信息 * * @param map * @param response */
public void exportToExcel(Map<String, Object> map, HttpServletResponse response) throws Exception { // 查詢得到用戶數據
List<UserVO> vos = findByConditionReturnExcel(map); // 定義表格頭信息
String[] titles = new String[]{"用戶名稱", "狀態", "創建時間", "創建人", "最后登錄時間", "所在公司"}; // 為表格內容賦值
String[][] values = new String[vos.size()][titles.length]; if (vos != null && vos.size() > 0) { for (int i = 0; i < vos.size(); i++) { values[i][0] = vos.get(i).getUserName(); values[i][1] = vos.get(i).getUserState(); values[i][2] = vos.get(i).getCreateDate(); values[i][3] = vos.get(i).getCreateUser(); values[i][4] = vos.get(i).getLastLoginTime(); values[i][5] = vos.get(i).getSourceCompanyName(); } } // 導出
ExcelUtil.returnClient(response, "user.xls", ExcelUtil.getHSSFWorkbook("用戶管理", titles, values)); }
HttpServletResponse 對象可在控制層通過參數傳遞進來即可。
那么問題來了,對於不是通過客戶端瀏覽器的導出操作,我們就無法用 HttpServletResponse,其實這也是我在做一個統計工具的時候遇到的問題,
這個統計用的是圖形界面開發SWT實現。那么我們就無法用這種客戶端響應的方式實現導出。那么這個工具類不是就沒用了?當時查了網上的資料,其實
只需更改響應方式即可。后來我用輸出流修改了這個工具類。改動如下(因為只是個統計小工具也就沒用到緩沖流之類的,只是簡單做了輸出)
fileName可定義具體的輸出位置如: 'D:/統計/'+文件名
//通過輸出流響應
public static void returnClient(String fileName, HSSFWorkbook wb) throws Exception { FileOutputStream os = null; try { File file = new File(fileName); os = new FileOutputStream(file); wb.write(os); os.flush(); os.close(); } catch (Exception e) { throw e; } finally { if (os != null) { try { // 關閉流
os.close(); } catch (IOException e) { throw e; } } } }
導出數據量大的解決辦法
使用poi導出excel的時候如果數據過多,超過65535條會報錯,因為excel2003一個sheet表最多導出65535條,excel2007是10萬4000多條限制。
Invalid row number (65536) outside allowable range (0..65535)
解決方案:一個sheet最多可以導出65535條,我們可以分成多個sheet導出。
當時我寫了一個特別low的方法,但是也算實現的功能。下面分享一下我的辦法
當時是這樣想的,比如從數據庫查詢出一百萬條數據,我把這數據分成多份,用同一個HSSFWorkbook對象,執行工具類中getHSSFWorkbook方法不就行了
於是就有了以下實現(主要是懶得改工具類-_-)
/** * 將一個集合拆分成多個 * * @param list 需要拆分的集合 * @param num 每個集合的數量 * @return
*/
public Map<String, List<CDM>> splitList(List<CDM> list, Integer num) { // list 長度
int listSize = list.size(); // 用map來存放集合
HashMap<String, List<CDM>> listHashMap = new HashMap<String, List<CDM>>(); // 單個集合對象
List<CDM> childList = new ArrayList<>(); // 遍歷要拆分集合按定義的num數量存放到childList
for (int i = 0; i < listSize; i++) { childList.add(list.get(i)); if (((i + 1) % num == 0) || (i + 1 == listSize)) { // 存入map
listHashMap.put(String.valueOf(i), childList); childList = new ArrayList<>(); } } return listHashMap; }
以上是拆分集合的方法,通過這個方法即可實現集合拆分,之后在業務代碼這樣寫就行了
wb為HSSFWorkbook對象,我們在代碼里把它在循環外聲明就行 HSSFWorkbook wb = new HSSFWorkbook();
if (list.size() > 65535) {
// 每個小集合放60000萬個,個數可以自己定義 Map<String, List<CDM>> childListMap = splitList(list, 60000);
// 循環map,循環為wb添加sheet wb也就是我們的HSSFWorkbook對象 for (Map.Entry<String, List<CDM>> entry : childListMap.entrySet()) {
String[][] values = new String[entry.getValue().size()][titles.length]; for (int i = 0; i < entry.getValue().size(); i++) { values[i][0] = entry.getValue().get(i).getId(); values[i][1] = entry.getValue().get(i).getName(); //....
} wb = ExcelUtil.getHSSFWorkbook("統計" + entry.getKey(), titles, values, wb); }
這樣就可以支持大數據量導出了。