java操縱excel檔案,實現excel匯入匯出
前言
java操縱excel檔案常用的有jxl和poi兩種方式,其中最主要的區別在於jxl不支援.xlsx,而poi支援.xlsx。
這裡介紹的使用poi方式,poi提供了HSSFWorkbook、XSSFWorkbook、SXSSFWorkbook。
HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,副檔名是.xls;
XSSFWorkbook:是操作Excel2007後的版本,副檔名是.xlsx;
SXSSFWorkbook:是操作Excel2007後的版本,副檔名是.xlsx;
第一種:HSSFWorkbook
poi匯出excel最常用的方式;但是此種方式的侷限就是匯出的行數至多為65535行,超出65536條後系統就會報錯。此方式因為行數不足七萬行所以一般不會發生記憶體不足的情況(OOM)。
第二種:XSSFWorkbook
這種形式的出現是為了突破HSSFWorkbook的65535行侷限。其對應的是excel2007(1048576行,16384列)副檔名為“.xlsx”,最多可以匯出104萬行,不過這樣就伴隨著一個問題---OOM記憶體溢位,原因是你所建立的book sheet row cell等此時是存在記憶體的並沒有持久化。
第三種:SXSSFWorkbook
從POI 3.8版本開始,提供了一種基於XSSF的低記憶體佔用的SXSSF方式。對於大型excel檔案的建立,一個關鍵問題就是,要確保不會記憶體溢位。其實,就算生成很小的excel(比如幾Mb),它用掉的記憶體是遠大於excel檔案實際的size的。如果單元格還有各種格式(比如,加粗,背景標紅之類的),那它佔用的記憶體就更多了。對於大型excel的建立且不會記憶體溢位的,就只有SXSSFWorkbook了。它的原理很簡單,用硬碟空間換記憶體(就像hash map用空間換時間一樣)。
SXSSFWorkbook是streaming版本的XSSFWorkbook,它只會儲存最新的excel rows在記憶體裡供檢視,在此之前的excel rows都會被寫入到硬盤裡(Windows電腦的話,是寫入到C盤根目錄下的temp資料夾)。被寫入到硬盤裡的rows是不可見的/不可訪問的。只有還儲存在記憶體裡的才可以被訪問到。
SXSSF與XSSF的對比:
- a. 在一個時間點上,只可以訪問一定數量的資料
- b. 不再支援Sheet.clone()
- c. 不再支援公式的求值
- d. 在使用Excel模板下載資料時將不能動態改變表頭,因為這種方式已經提前把excel寫到硬碟的了就不能再改了
當資料量超出65536條後,在使用HSSFWorkbook或XSSFWorkbook,程式會報OutOfMemoryError:Javaheap space;記憶體溢位錯誤。這時應該用SXSSFworkbook。
實現步驟
本次使用XSSFWorkbook,HSSFWorkbook實現請移步:使用HSSFWorkbook匯出、操作excel
建立流程:(上級為 下級的載體)
1:.建立 工作簿
2.建立 sheet(可以建立多個)
3.建立行
4.建立單元格
引入相關依賴:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency>
讀:
/** * 讀取Excel檔案的內容 * @param inputStream excel檔案,以InputStream的形式傳入 * @param sheetName sheet名字 * @return 以List返回excel中內容 */ public static List<Map<String, String>> readExcel(InputStream inputStream, String sheetName) { //定義工作簿 XSSFWorkbook xssfWorkbook = null; try { xssfWorkbook = new XSSFWorkbook(inputStream); } catch (Exception e) { System.out.println("Excel data file cannot be found!"); } //定義工作表 XSSFSheet xssfSheet; if (sheetName.equals("")) { // 預設取第一個子表 xssfSheet = xssfWorkbook.getSheetAt(0); } else { xssfSheet = xssfWorkbook.getSheet(sheetName); } List<Map<String, String>> list = new ArrayList<Map<String, String>>(); //定義行 //預設第一行為標題行,index = 0 XSSFRow titleRow = xssfSheet.getRow(0); //迴圈取每行的資料 for (int rowIndex = 1; rowIndex < xssfSheet.getPhysicalNumberOfRows(); rowIndex++) { XSSFRow xssfRow = xssfSheet.getRow(rowIndex); if (xssfRow == null) { continue; } Map<String, String> map = new LinkedHashMap<String, String>(); //迴圈取每個單元格(cell)的資料 for (int cellIndex = 0; cellIndex < xssfRow.getPhysicalNumberOfCells(); cellIndex++) { XSSFCell titleCell = titleRow.getCell(cellIndex); XSSFCell xssfCell = xssfRow.getCell(cellIndex); map.put(getString(titleCell),getString(xssfCell)); } list.add(map); } return list; } /** * 把單元格的內容轉為字串 * @param xssfCell 單元格 * @return 字串 */ public static String getString(XSSFCell xssfCell) { if (xssfCell == null) { return ""; } if (xssfCell.getCellTypeEnum() == CellType.NUMERIC) { return String.valueOf(xssfCell.getNumericCellValue()); } else if (xssfCell.getCellTypeEnum() == CellType.BOOLEAN) { return String.valueOf(xssfCell.getBooleanCellValue()); } else { return xssfCell.getStringCellValue(); } }
寫:
/** * 把內容寫入Excel * @param list 傳入要寫的內容,此處以一個List內容為例,先把要寫的內容放到一個list中 * @param outputStream 把輸出流懟到要寫入的Excel上,準備往裡面寫資料 */ public static void writeExcel(List<List> list, OutputStream outputStream) { //建立工作簿 XSSFWorkbook xssfWorkbook = null; xssfWorkbook = new XSSFWorkbook(); //建立工作表 XSSFSheet xssfSheet; xssfSheet = xssfWorkbook.createSheet(); //建立行 XSSFRow xssfRow; //建立列,即單元格Cell XSSFCell xssfCell; //把List裡面的資料寫到excel中 for (int i=0;i<list.size();i++) { //從第一行開始寫入 xssfRow = xssfSheet.createRow(i); //建立每個單元格Cell,即列的資料 List sub_list =list.get(i); for (int j=0;j<sub_list.size();j++) { xssfCell = xssfRow.createCell(j); //建立單元格 xssfCell.setCellValue((String)sub_list.get(j)); //設定單元格內容 } } //用輸出流寫到excel try { xssfWorkbook.write(outputStream); outputStream.flush(); outputStream.close(); }catch (IOException e) { e.printStackTrace(); } }
工具類:
附:把一個Map中的所有鍵和值分別放到一個list中,再把這兩個list整個放到一個大的list裡面,即 [ [key1,key2,key3...] , [value1,value2,value3...] ]
public static List<List> convertMapToList(Map map) { List<List> list = new ArrayList<List>(); List<String> key_list = new LinkedList<String>(); List<String> value_list = new LinkedList<String>(); Set<Entry<String,String>> set = map.entrySet(); Iterator<Entry<String,String>> iter1 = set.iterator(); while (iter1.hasNext()) { key_list.add(iter1.next().getKey()); } list.add(key_list); Collection<String> value = map.values(); Iterator<String> iter2 = value.iterator(); while (iter2.hasNext()) { value_list.add(iter2.next()); } list.add(value_list); return list; }