POI實現大資料EXCLE匯入匯出,解決記憶體溢出問題

來源:互聯網
上載者:User

標籤:cto   元素   tor   void   工作   openxml   XML   lld   contents   

  使用POI能夠匯出大資料保證記憶體不溢出的一個重要原因是SXSSFWorkbook產生的EXCEL為2007版本,修改EXCEL2007檔案尾碼為ZIP開啟可以看到,每一個Sheet都是一個xml檔案,儲存格格式和儲存格座標均用標籤表示。直接使用SXSSFWorkbook來到匯出EXCEL本身就是POI為了大資料量匯出而量身定製的,所以匯出可以直接使用SXSSFWorkbook方式。

  為了保險起見可以採用多Sheet的方式保證記憶體不溢出。需要注意的是Sheet名稱不能重複;下載的時候需要定義好返回頭。

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

  匯出EXCEL較為簡單,建立Workbook對象和Sheet對象往裡塞值就行了。但是匯入讀取EXCEL的時候SXSSFWorkbook沒有讀取檔案流的方法,只能使用XSSFWorkbook來讀取,幾千條資料可能就記憶體溢出了。

  這時候就要使用OPCPackage

public static OPCPackage open(java.io.InputStream in)                       throws InvalidFormatException,                              java.io.IOExceptionOpen a package. Note - uses quite a bit more memory than open(String), which doesn‘t need to hold the whole zip file in memory, and can take advantage of native methodsParameters:    in - The InputStream to read the package fromReturns:    A PackageBase objectThrows:    InvalidFormatException    java.io.IOException

  POI給出的API表示使用OPCPackage不需要將檔案完全讀取到記憶體中。

  調用方法

File file = uploadFile.getFile();InputStream is = new FileInputStream(file);excelReader.readInputStream(is);excelReader.process();

  ExcelReader.java

/** * 抽象Excel2007讀取器,excel2007的底層資料結構是xml檔案,採用SAX的事件驅動的方法解析 * xml,需要繼承DefaultHandler,在遇到檔案內容時,事件會觸發,這種做法可以大大降低 * 記憶體的耗費,特別使用於大資料量的檔案。 * */public class Excel2007Reader extends DefaultHandler {    //共用字串表    private SharedStringsTable sst;    //上一次的內容    private String lastContents;    private boolean nextIsString;    private int sheetIndex = -1;    private List<String> rowlist = new ArrayList<String>();    //當前行    private int curRow = 0;    //當前列    private int curCol = 0;    //日期標誌    private boolean dateFlag;    //數字標誌    private boolean numberFlag;        private boolean isTElement;        private IRowReader rowReader;        public void setRowReader(IRowReader rowReader){        this.rowReader = rowReader;    }        /**只遍曆一個試算表,其中sheetId為要遍曆的sheet索引,從1開始,1-3     * @param filename     * @param sheetId     * @throws Exception     */    public void processOneSheet(String filename,int sheetId) throws Exception {        OPCPackage pkg = OPCPackage.open(filename);        XSSFReader r = new XSSFReader(pkg);        SharedStringsTable sst = r.getSharedStringsTable();        XMLReader parser = fetchSheetParser(sst);                // 根據 rId# 或 rSheet# 尋找sheet        InputStream sheet2 = r.getSheet("rId"+sheetId);        sheetIndex++;        InputSource sheetSource = new InputSource(sheet2);        parser.parse(sheetSource);        sheet2.close();    }    /**     * 遍曆活頁簿中所有的試算表     * @param filename     * @throws Exception     */    public void process(String filename) throws Exception {        OPCPackage pkg = OPCPackage.open(filename);        XSSFReader r = new XSSFReader(pkg);        SharedStringsTable sst = r.getSharedStringsTable();        XMLReader parser = fetchSheetParser(sst);        Iterator<InputStream> sheets = r.getSheetsData();        while (sheets.hasNext()) {            curRow = 0;            sheetIndex++;            InputStream sheet = sheets.next();            InputSource sheetSource = new InputSource(sheet);            parser.parse(sheetSource);            sheet.close();        }    }    public XMLReader fetchSheetParser(SharedStringsTable sst)            throws SAXException {        XMLReader parser = XMLReaderFactory                .createXMLReader("org.apache.xerces.parsers.SAXParser");        this.sst = sst;        parser.setContentHandler(this);        return parser;    }    public void startElement(String uri, String localName, String name,            Attributes attributes) throws SAXException {                // c => 儲存格        if ("c".equals(name)) {            // 如果下一個元素是 SST 的索引,則將nextIsString標記為true            String cellType = attributes.getValue("t");            if ("s".equals(cellType)) {                nextIsString = true;            } else {                nextIsString = false;            }            //日期格式            String cellDateType = attributes.getValue("s");            if ("1".equals(cellDateType)){                dateFlag = true;            } else {                dateFlag = false;            }            String cellNumberType = attributes.getValue("s");            if("2".equals(cellNumberType)){                numberFlag = true;            } else {                numberFlag = false;            }                    }        //當元素為t時        if("t".equals(name)){            isTElement = true;        } else {            isTElement = false;        }                // 置空        lastContents = "";    }    public void endElement(String uri, String localName, String name)            throws SAXException {                // 根據SST的索引值的到儲存格的真正要儲存的字串        // 這時characters()方法可能會被調用多次        if (nextIsString) {            try {                int idx = Integer.parseInt(lastContents);                lastContents = new XSSFRichTextString(sst.getEntryAt(idx))                        .toString();            } catch (Exception e) {            }        }         //t元素也包含字串        if(isTElement){            String value = lastContents.trim();            rowlist.add(curCol, value);            curCol++;            isTElement = false;            // v => 儲存格的值,如果儲存格是字串則v標籤的值為該字串在SST中的索引            // 將儲存格內容加入rowlist中,在這之前先去掉字串前後的空白符        } else if ("v".equals(name)) {            String value = lastContents.trim();            value = value.equals("")?" ":value;            //日期格式處理            if(dateFlag){                 Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));                 SimpleDateFormat dateFormat = new SimpleDateFormat(                 "dd/MM/yyyy");                 value = dateFormat.format(date);            }             //數字類型處理            if(numberFlag){                BigDecimal bd = new BigDecimal(value);                value = bd.setScale(3,BigDecimal.ROUND_UP).toString();            }            rowlist.add(curCol, value);            curCol++;        }else {            //如果標籤名稱為 row ,這說明已到行尾,調用 optRows() 方法            if (name.equals("row")) {                rowReader.getRows(sheetIndex,curRow,rowlist);                rowlist.clear();                curRow++;                curCol = 0;            }        }            }    public void characters(char[] ch, int start, int length)            throws SAXException {        //得到儲存格內容的值        lastContents += new String(ch, start, length);    }}

 

POI實現大資料EXCLE匯入匯出,解決記憶體溢出問題

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.