1. 程式人生 > 其它 >java操縱excel檔案,實現excel匯入匯出

java操縱excel檔案,實現excel匯入匯出

前言

java操縱excel檔案常用的有jxlpoi兩種方式,其中最主要的區別在於jxl不支援.xlsx,而poi支援.xlsx

這裡介紹的使用poi方式,poi提供了HSSFWorkbookXSSFWorkbookSXSSFWorkbook

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;
    }