工做中經常會遇到一些圖表須要導出的功能,在這裏本身寫了一個工具類方便之後使用(使用POI實現)。java
項目依賴數據庫
<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導出功能。apache
下面是一個導出的例子瀏覽器
/** * 導出用戶信息 * * @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多條限制。app
Invalid row number (65536) outside allowable range (0..65535)
解決方案:一個sheet最多能夠導出65535條,咱們能夠分紅多個sheet導出。
當時我寫了一個特別蠢的方法,可是也算實現的功能。下面分享一下個人辦法
當時是這樣想的,好比從數據庫查詢出一百萬條數據,我把這數據分紅多份,用同一個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("各個區局CDM點播數據準確率統計" + entry.getKey(), titles, values, wb); }
這樣就能夠支持大數據量導出了。ide
感受這個解決辦法有點蠢,等有時間修改一下工具類再進行補充。工具