欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

POI实现超大数据的Excel的读写操作,支持Excel最大行数 博客分类: Java  

程序员文章站 2024-03-24 14:38:34
...

参考:http://thinkgem.iteye.com/blog/2150940

前端时间写了注解方式Excel的读取和写入,它是根据注解完成Excel的操作,虽说支持大数据,但对于超大数据就无能为力了,因为它的读写期间都是将所有数据放入系统内存的,除非你有超大的内存。

 

因项目需要对超大数据的Excel读写操作,于是网上找了个超大数据的读写代码,这个不需要太大内存。并对此进行了简单的修改。

 

原理如下:

 

Excel超大数据读取:抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析 xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低内存的耗费,特别使用于大数据量的文件。

 

Excel超大数据写入:抽象excel2007读入器,先构建.xlsx一张模板,改写模板中的sheet.xml, 使用这种方法 写入.xlsx文件,不需要太大的内存。

 

先看调用示例:

  

Java代码  POI实现超大数据的Excel的读写操作,支持Excel最大行数
            
    
    博客分类: Java  
  1. String file = "E:/导入测试数据.xlsx";  
  2.   
  3. ExcelReader reader = new ExcelReader() {  
  4.     public void getRows(int sheetIndex, int curRow, List<String> rowList) {  
  5.           
  6.         System.out.println("Sheet:" + sheetIndex + ", Row:" + curRow + ", Data:" +rowList);  
  7.           
  8.     }  
  9. };  
  10. reader.process(file, 1);  

 

Java代码  POI实现超大数据的Excel的读写操作,支持Excel最大行数
            
    
    博客分类: Java  
  1. String file = "E:/导出测试数据.xlsx";  
  2.   
  3. ExcelWriter writer = new ExcelWriter() {  
  4.     public void generate() throws Exception {  
  5.           
  6.         // 电子表格开始  
  7.         this.beginSheet();  
  8.           
  9.         for (int rownum = 0; rownum < 100; rownum++) {  
  10.             // 插入新行  
  11.             this.insertRow(rownum);  
  12.               
  13.             // 建立新单元格,索引值从0开始,表示第一列  
  14.             this.createCell(0, "第 " + rownum + " 行");  
  15.             this.createCell(1, 34343.123456789);  
  16.             this.createCell(2, "23.67%");  
  17.             this.createCell(3, "12:12:23");  
  18.             this.createCell(4, "2014-10-11 12:12:23");  
  19.             this.createCell(5, "true");  
  20.             this.createCell(6, "false");  
  21.   
  22.             // 结束行  
  23.             this.endRow();  
  24.         }  
  25.           
  26.         // 电子表格结束  
  27.             this.endSheet();  
  28.         }  
  29.     };  
  30.     writer.process(file);  
  31. }  

 这里只展示了对数据的读取和写入,如果正式保存到数据库时建议读取一部分(如100条)再写入一次数据库,尽量不要读取一条就写入一条,这样会非常耗费资源。

 

源代码如下:

 

Java代码  POI实现超大数据的Excel的读写操作,支持Excel最大行数
            
    
    博客分类: Java  
  1. import java.io.InputStream;  
  2. import java.math.BigDecimal;  
  3. import java.text.SimpleDateFormat;  
  4. import java.util.ArrayList;  
  5. import java.util.Date;  
  6. import java.util.Iterator;  
  7. import java.util.List;  
  8.   
  9. import org.apache.poi.hssf.usermodel.HSSFDateUtil;  
  10. import org.apache.poi.openxml4j.opc.OPCPackage;  
  11. import org.apache.poi.xssf.eventusermodel.XSSFReader;  
  12. import org.apache.poi.xssf.model.SharedStringsTable;  
  13. import org.apache.poi.xssf.usermodel.XSSFRichTextString;  
  14. import org.xml.sax.Attributes;  
  15. import org.xml.sax.InputSource;  
  16. import org.xml.sax.SAXException;  
  17. import org.xml.sax.XMLReader;  
  18. import org.xml.sax.helpers.DefaultHandler;  
  19. import org.xml.sax.helpers.XMLReaderFactory;  
  20.   
  21. /** 
  22.  * Excel超大数据读取,抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析 
  23.  * xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低 内存的耗费,特别使用于大数据量的文件。 
  24.  * @version 2014-9-2 
  25.  */  
  26. public abstract class ExcelReader extends DefaultHandler {  
  27.   
  28.     // 共享字符串表  
  29.     private SharedStringsTable sst;  
  30.       
  31.     // 上一次的内容  
  32.     private String lastContents;  
  33.     private boolean nextIsString;  
  34.   
  35.     private int sheetIndex = -1;  
  36.     private List<String> rowList = new ArrayList<String>();  
  37.   
  38.     // 当前行  
  39.     private int curRow = 0;  
  40.     // 当前列  
  41.     private int curCol = 0;  
  42.     // 日期标志  
  43.     private boolean dateFlag;  
  44.     // 数字标志  
  45.     private boolean numberFlag;  
  46.   
  47.     private boolean isTElement;  
  48.   
  49.     /** 
  50.      * 遍历工作簿中所有的电子表格 
  51.      * @param filename 
  52.      * @throws Exception 
  53.      */  
  54.     public void process(String filename) throws Exception {  
  55.         OPCPackage pkg = OPCPackage.open(filename);  
  56.         XSSFReader r = new XSSFReader(pkg);  
  57.         SharedStringsTable sst = r.getSharedStringsTable();  
  58.         XMLReader parser = fetchSheetParser(sst);  
  59.         Iterator<InputStream> sheets = r.getSheetsData();  
  60.         while (sheets.hasNext()) {  
  61.             curRow = 0;  
  62.             sheetIndex++;  
  63.             InputStream sheet = sheets.next();  
  64.             InputSource sheetSource = new InputSource(sheet);  
  65.             parser.parse(sheetSource);  
  66.             sheet.close();  
  67.         }  
  68.     }  
  69.   
  70.     /** 
  71.      * 只遍历一个电子表格,其中sheetId为要遍历的sheet索引,从1开始,1-3 
  72.      * @param filename 
  73.      * @param sheetId 
  74.      * @throws Exception 
  75.      */  
  76.     public void process(String filename, int sheetId) throws Exception {  
  77.         OPCPackage pkg = OPCPackage.open(filename);  
  78.         XSSFReader r = new XSSFReader(pkg);  
  79.         SharedStringsTable sst = r.getSharedStringsTable();  
  80.         XMLReader parser = fetchSheetParser(sst);  
  81.         // 根据 rId# 或 rSheet# 查找sheet  
  82.         InputStream sheet2 = r.getSheet("rId" + sheetId);  
  83.         sheetIndex++;  
  84.         InputSource sheetSource = new InputSource(sheet2);  
  85.         parser.parse(sheetSource);  
  86.         sheet2.close();  
  87.     }  
  88.   
  89.     public XMLReader fetchSheetParser(SharedStringsTable sst)  
  90.             throws SAXException {  
  91.         XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");  
  92.         this.sst = sst;  
  93.         parser.setContentHandler(this);  
  94.         return parser;  
  95.     }  
  96.   
  97.     public void startElement(String uri, String localName, String name,  
  98.             Attributes attributes) throws SAXException {  
  99.   
  100. //      System.out.println("startElement: " + localName + ", " + name + ", " + attributes);  
  101.   
  102.         // c => 单元格  
  103.         if ("c".equals(name)) {  
  104.             // 如果下一个元素是 SST 的索引,则将nextIsString标记为true  
  105.             String cellType = attributes.getValue("t");  
  106.             if ("s".equals(cellType)) {  
  107.                 nextIsString = true;  
  108.             } else {  
  109.                 nextIsString = false;  
  110.             }  
  111.             // 日期格式  
  112.             String cellDateType = attributes.getValue("s");  
  113.             if ("1".equals(cellDateType)) {  
  114.                 dateFlag = true;  
  115.             } else {  
  116.                 dateFlag = false;  
  117.             }  
  118.             String cellNumberType = attributes.getValue("s");  
  119.             if ("2".equals(cellNumberType)) {  
  120.                 numberFlag = true;  
  121.             } else {  
  122.                 numberFlag = false;  
  123.             }  
  124.   
  125.         }  
  126.         // 当元素为t时  
  127.         if ("t".equals(name)) {  
  128.             isTElement = true;  
  129.         } else {  
  130.             isTElement = false;  
  131.         }  
  132.   
  133.         // 置空  
  134.         lastContents = "";  
  135.     }  
  136.   
  137.     public void endElement(String uri, String localName, String name)  
  138.             throws SAXException {  
  139.   
  140. //      System.out.println("endElement: " + localName + ", " + name);  
  141.   
  142.         // 根据SST的索引值的到单元格的真正要存储的字符串  
  143.         // 这时characters()方法可能会被调用多次  
  144.         if (nextIsString) {  
  145.             try {  
  146.                 int idx = Integer.parseInt(lastContents);  
  147.                 lastContents = new XSSFRichTextString(sst.getEntryAt(idx))  
  148.                         .toString();  
  149.             } catch (Exception e) {  
  150.   
  151.             }  
  152.         }  
  153.         // t元素也包含字符串  
  154.         if (isTElement) {  
  155.             String value = lastContents.trim();  
  156.             rowList.add(curCol, value);  
  157.             curCol++;  
  158.             isTElement = false;  
  159.             // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引  
  160.             // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符  
  161.         } else if ("v".equals(name)) {  
  162.             String value = lastContents.trim();  
  163.             value = value.equals("") ? " " : value;  
  164.             try {  
  165.                 // 日期格式处理  
  166.                 if (dateFlag) {  
  167.                     Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));  
  168.                     SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");  
  169.                     value = dateFormat.format(date);  
  170.                 }  
  171.                 // 数字类型处理  
  172.                 if (numberFlag) {  
  173.                     BigDecimal bd = new BigDecimal(value);  
  174.                     value = bd.setScale(3, BigDecimal.ROUND_UP).toString();  
  175.                 }  
  176.             } catch (Exception e) {  
  177.                 // 转换失败仍用读出来的值  
  178.             }  
  179.             rowList.add(curCol, value);  
  180.             curCol++;  
  181.         } else {  
  182.             // 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法  
  183.             if (name.equals("row")) {  
  184.                 getRows(sheetIndex + 1, curRow, rowList);  
  185.                 rowList.clear();  
  186.                 curRow++;  
  187.                 curCol = 0;  
  188.             }  
  189.         }  
  190.   
  191.     }  
  192.   
  193.     public void characters(char[] ch, int start, int length)  
  194.             throws SAXException {  
  195.         // 得到单元格内容的值  
  196.         lastContents += new String(ch, start, length);  
  197.     }  
  198.   
  199.     /** 
  200.      * 获取行数据回调 
  201.      * @param sheetIndex 
  202.      * @param curRow 
  203.      * @param rowList 
  204.      */  
  205.     public abstract void getRows(int sheetIndex, int curRow, List<String> rowList);  
  206.   
  207.     /** 
  208.      * 测试方法 
  209.      */  
  210.     public static void main(String[] args) throws Exception {  
  211.   
  212. String file = "E:/导入测试数据.xlsx";  
  213.   
  214. ExcelReader reader = new ExcelReader() {  
  215.     public void getRows(int sheetIndex, int curRow, List<String> rowList) {  
  216.           
  217.         System.out.println("Sheet:" + sheetIndex + ", Row:" + curRow + ", Data:" +rowList);  
  218.           
  219.     }  
  220. };  
  221. reader.process(file, 1);  
  222.   
  223.     }  
  224.   
  225. }  

 

 

Java代码  POI实现超大数据的Excel的读写操作,支持Excel最大行数
            
    
    博客分类: Java  
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.FileWriter;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7. import java.io.OutputStream;  
  8. import java.io.Writer;  
  9. import java.util.Calendar;  
  10. import java.util.Enumeration;  
  11. import java.util.zip.ZipEntry;  
  12. import java.util.zip.ZipFile;  
  13. import java.util.zip.ZipOutputStream;  
  14.   
  15. import org.apache.poi.hssf.util.CellReference;  
  16. import org.apache.poi.ss.usermodel.DateUtil;  
  17. import org.apache.poi.xssf.usermodel.XSSFSheet;  
  18. import org.apache.poi.xssf.usermodel.XSSFWorkbook;  
  19.   
  20. /** 
  21.  * Excel超大数据写入,抽象excel2007读入器,先构建.xlsx一张模板,改写模板中的sheet.xml, 
  22.  * 使用这种方法 写入.xlsx文件,不需要太大的内存 
  23.  * @version 2014-9-2 
  24.  */  
  25. public abstract class ExcelWriter {  
  26.   
  27.     private SpreadsheetWriter sw;  
  28.   
  29.     /** 
  30.      * 写入电子表格的主要流程 
  31.      *  
  32.      * @param fileName 
  33.      * @throws Exception 
  34.      */  
  35.     public void process(String fileName) throws Exception {  
  36.           
  37.         // 建立工作簿和电子表格对象  
  38.         XSSFWorkbook wb = new XSSFWorkbook();  
  39.         XSSFSheet sheet = wb.createSheet("sheet1");  
  40.           
  41.         // 持有电子表格数据的xml文件名 例如 /xl/worksheets/sheet1.xml  
  42.         String sheetRef = sheet.getPackagePart().getPartName().getName();  
  43.   
  44.         // 保存模板  
  45.         FileOutputStream os = new FileOutputStream("template.xlsx");  
  46.         wb.write(os);  
  47.         os.close();  
  48.   
  49.         // 生成xml文件  
  50.         File tmp = File.createTempFile("sheet", ".xml");  
  51.         Writer fw = new FileWriter(tmp);  
  52.         sw = new SpreadsheetWriter(fw);  
  53.         generate();  
  54.         fw.close();  
  55.   
  56.         // 使用产生的数据替换模板  
  57.         File templateFile = new File("template.xlsx");  
  58.         FileOutputStream out = new FileOutputStream(fileName);  
  59.         substitute(templateFile, tmp, sheetRef.substring(1), out);  
  60.         out.close();  
  61.         // 删除文件之前调用一下垃圾回收器,否则无法删除模板文件  
  62.         System.gc();  
  63.         // 删除临时模板文件  
  64.         if (templateFile.isFile() && templateFile.exists()) {  
  65.             templateFile.delete();  
  66.         }  
  67.     }  
  68.   
  69.     /** 
  70.      * 类使用者应该使用此方法进行写操作 
  71.      *  
  72.      * @throws Exception 
  73.      */  
  74.     public abstract void generate() throws Exception;  
  75.   
  76.     public void beginSheet() throws IOException {  
  77.         sw.beginSheet();  
  78.     }  
  79.   
  80.     public void insertRow(int rowNum) throws IOException {  
  81.         sw.insertRow(rowNum);  
  82.     }  
  83.   
  84.     public void createCell(int columnIndex, String value) throws IOException {  
  85.         sw.createCell(columnIndex, value, -1);  
  86.     }  
  87.   
  88.     public void createCell(int columnIndex, double value) throws IOException {  
  89.         sw.createCell(columnIndex, value, -1);  
  90.     }  
  91.   
  92.     public void endRow() throws IOException {  
  93.         sw.endRow();  
  94.     }  
  95.   
  96.     public void endSheet() throws IOException {  
  97.         sw.endSheet();  
  98.     }  
  99.   
  100.     /** 
  101.      *  
  102.      * @param zipfile 
  103.      *            the template file 
  104.      * @param tmpfile 
  105.      *            the XML file with the sheet data 
  106.      * @param entry 
  107.      *            the name of the sheet entry to substitute, e.g. 
  108.      *            xl/worksheets/sheet1.xml 
  109.      * @param out 
  110.      *            the stream to write the result to 
  111.      */  
  112.     private static void substitute(File zipfile, File tmpfile, String entry,  
  113.             OutputStream out) throws IOException {  
  114.         ZipFile zip = new ZipFile(zipfile);  
  115.         ZipOutputStream zos = new ZipOutputStream(out);  
  116.   
  117.         @SuppressWarnings("unchecked")  
  118.         Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries();  
  119.         while (en.hasMoreElements()) {  
  120.             ZipEntry ze = en.nextElement();  
  121.             if (!ze.getName().equals(entry)) {  
  122.                 zos.putNextEntry(new ZipEntry(ze.getName()));  
  123.                 InputStream is = zip.getInputStream(ze);  
  124.                 copyStream(is, zos);  
  125.                 is.close();  
  126.             }  
  127.         }  
  128.         zos.putNextEntry(new ZipEntry(entry));  
  129.         InputStream is = new FileInputStream(tmpfile);  
  130.         copyStream(is, zos);  
  131.         is.close();  
  132.         zos.close();  
  133.     }  
  134.   
  135.     private static void copyStream(InputStream in, OutputStream out)  
  136.             throws IOException {  
  137.         byte[] chunk = new byte[1024];  
  138.         int count;  
  139.         while ((count = in.read(chunk)) >= 0) {  
  140.             out.write(chunk, 0, count);  
  141.         }  
  142.     }  
  143.   
  144.     /** 
  145.      * 在写入器中写入电子表格 
  146.      *  
  147.      */  
  148.     public static class SpreadsheetWriter {  
  149.         private final Writer _out;  
  150.         private int _rownum;  
  151.         private static String LINE_SEPARATOR = System  
  152.                 .getProperty("line.separator");  
  153.   
  154.         public SpreadsheetWriter(Writer out) {  
  155.             _out = out;  
  156.         }  
  157.   
  158.         public void beginSheet() throws IOException {  
  159.             _out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"  
  160.                     + "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");  
  161.             _out.write("<sheetData>" + LINE_SEPARATOR);  
  162.         }  
  163.   
  164.         public void endSheet() throws IOException {  
  165.             _out.write("</sheetData>");  
  166.             _out.write("</worksheet>");  
  167.         }  
  168.   
  169.         /** 
  170.          * 插入新行 
  171.          *  
  172.          * @param rownum 
  173.          *            以0开始 
  174.          */  
  175.         public void insertRow(int rownum) throws IOException {  
  176.             _out.write("<row r=\"" + (rownum + 1) + "\">" + LINE_SEPARATOR);  
  177.             this._rownum = rownum;  
  178.         }  
  179.   
  180.         /** 
  181.          * 插入行结束标志 
  182.          */  
  183.         public void endRow() throws IOException {  
  184.             _out.write("</row>" + LINE_SEPARATOR);  
  185.         }  
  186.   
  187.         /** 
  188.          * 插入新列 
  189.          *  
  190.          * @param columnIndex 
  191.          * @param value 
  192.          * @param styleIndex 
  193.          * @throws IOException 
  194.          */  
  195.         public void createCell(int columnIndex, String value, int styleIndex)  
  196.                 throws IOException {  
  197.             String ref = new CellReference(_rownum, columnIndex)  
  198.                     .formatAsString();  
  199.             _out.write("<c r=\"" + ref + "\" t=\"inlineStr\"");  
  200.             if (styleIndex != -1)  
  201.                 _out.write(" s=\"" + styleIndex + "\"");  
  202.             _out.write(">");  
  203.             _out.write("<is><t>" + encoderXML(value) + "</t></is>");  
  204.             _out.write("</c>");  
  205.         }  
  206.   
  207.         public void createCell(int columnIndex, String value)  
  208.                 throws IOException {  
  209.             createCell(columnIndex, value, -1);  
  210.         }  
  211.   
  212.         public void createCell(int columnIndex, double value, int styleIndex)  
  213.                 throws IOException {  
  214.             String ref = new CellReference(_rownum, columnIndex)  
  215.                     .formatAsString();  
  216.             _out.write("<c r=\"" + ref + "\" t=\"n\"");  
  217.             if (styleIndex != -1)  
  218.                 _out.write(" s=\"" + styleIndex + "\"");  
  219.             _out.write(">");  
  220.             _out.write("<v>" + value + "</v>");  
  221.             _out.write("</c>");  
  222.         }  
  223.   
  224.         public void createCell(int columnIndex, double value)  
  225.                 throws IOException {  
  226.             createCell(columnIndex, value, -1);  
  227.         }  
  228.   
  229.         public void createCell(int columnIndex, Calendar value, int styleIndex)  
  230.                 throws IOException {  
  231.             createCell(columnIndex, DateUtil.getExcelDate(value, false),  
  232.                     styleIndex);  
  233.         }  
  234.     }  
  235.   
  236.     // XML Encode  
  237.     private static final String[] xmlCode = new String[256];  
  238.   
  239.     static {  
  240.         // Special characters  
  241.         xmlCode['\''] = "'";  
  242.         xmlCode['\"'] = "\""; // double quote  
  243.         xmlCode['&'] = "&"; // ampersand  
  244.         xmlCode['<'] = "<"; // lower than  
  245.         xmlCode['>'] = ">"; // greater than  
  246.     }  
  247.   
  248.     /** 
  249.      * <p> 
  250.      * Encode the given text into xml. 
  251.      * </p> 
  252.      *  
  253.      * @param string 
  254.      *            the text to encode 
  255.      * @return the encoded string 
  256.      */  
  257.     public static String encoderXML(String string) {  
  258.         if (string == null)  
  259.             return "";  
  260.         int n = string.length();  
  261.         char character;  
  262.         String xmlchar;  
  263.         StringBuffer buffer = new StringBuffer();  
  264.         // loop over all the characters of the String.  
  265.         for (int i = 0; i < n; i++) {  
  266.             character = string.charAt(i);  
  267.             // the xmlcode of these characters are added to a StringBuffer  
  268.             // one by one  
  269.             try {  
  270.                 xmlchar = xmlCode[character];  
  271.                 if (xmlchar == null) {  
  272.                     buffer.append(character);  
  273.                 } else {  
  274.                     buffer.append(xmlCode[character]);  
  275.                 }  
  276.             } catch (ArrayIndexOutOfBoundsException aioobe) {  
  277.                 buffer.append(character);  
  278.             }  
  279.         }  
  280.         return buffer.toString();  
  281.     }  
  282.   
  283.     /** 
  284.      * 测试方法 
  285.      */  
  286.     public static void main(String[] args) throws Exception {  
  287.   
  288.     String file = "E:/导出测试数据.xlsx";  
  289.       
  290.     ExcelWriter writer = new ExcelWriter() {  
  291.         public void generate() throws Exception {  
  292.               
  293.             // 电子表格开始  
  294.             this.beginSheet();  
  295.               
  296.             for (int rownum = 0; rownum < 100; rownum++) {  
  297.                 // 插入新行  
  298.                 this.insertRow(rownum);  
  299.                   
  300.                 // 建立新单元格,索引值从0开始,表示第一列  
  301.                 this.createCell(0, "第 " + rownum + " 行");  
  302.                 this.createCell(1, 34343.123456789);  
  303.                 this.createCell(2, "23.67%");  
  304.                 this.createCell(3, "12:12:23");  
  305.                 this.createCell(4, "2014-10-11 12:12:23");  
  306.                 this.createCell(5, "true");  
  307.                 this.createCell(6, "false");  
  308.   
  309.                 // 结束行  
  310.                 this.endRow();  
  311.             }  
  312.               
  313.             // 电子表格结束  
  314.             this.endSheet();  
  315.         }  
  316.     };  
  317.     writer.process(file);  
  318. }  
  319.           
  320. }