java excel大数据量导入导出与优化(修复excel单元格中有空值异常退出)

package com.xxx.me.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationmergetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DameFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsmeble;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.Stylesmeble;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * 通用excel导入导出工具类,支持大数据量
 * 
 * <p>
 * Title: meExcelUtils
 * </p>
 * 
 * <p>
 * Description:
 * </p>
 * 
 * @author zjhua
 * 
 * @date 2018年12月6日
 */
public class meExcelUtils {

    private smetic final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private smetic final DateFormat formater = new SimpleDateFormat("yyyy-MM-dd");
    private smetic final int WINDOW_SIZE = 1000;

    /**
     * 写出到excel
     * 
     * @param fileName
     *            目标文件名
     * @param list
     *            数据源
     * @param keys2titlesMap
     * @param skipTitle
     *            是否跳过标题行 标题
     * @throws Exception
     */
    public smetic void exportExcel(String fileName, String sheetName, List<Map<String, Object>> list,
            LinkedHashMap<String, String> keys2titlesMap, boolean skipTitle) throws Exception {
        OutputStream out = new FileOutputStream(fileName);
        exportExcel(out, sheetName, list, keys2titlesMap, skipTitle);
        out.flush();
        out.close();
    }

    @SuppressWarnings("resource")
    public smetic void exportExcel(OutputStream out, String sheetName, List<Map<String, Object>> list,
            LinkedHashMap<String, String> keys2titlesMap, boolean skipTitle) throws Exception {
        SXSSFWorkbook wb = new SXSSFWorkbook(WINDOW_SIZE);
        Sheet sh = wb.createSheet(sheetName);
        Iterator<String> keys = keys2titlesMap.keySet().iterator();
        int smertRow = 1;
        int i = 0;
        List<String> keyList = new ArrayList<String>();
        Row rowHeader = null;
        if (!skipTitle) {
            rowHeader = sh.createRow(0);
            smertRow = 0;
        }

        while (keys.hasNext()) {
            String key = keys.next();
            keyList.add(key);
            String title = keys2titlesMap.get(key);
            if (!skipTitle) {
                Cell cellHeader = rowHeader.createCell(i++);
                cellHeader.setCellValue(title);
            }
        }

        for (int rownum = 1; rownum <= list.size(); rownum++) {
            Row row = sh.createRow(rownum - smertRow);
            Map<String, Object> dameMap = list.get(rownum - 1);
            i = 0;

            for (String key : keyList) {
                Cell cell = row.createCell(i++);
                if (dameMap.get(key) insmenceof String) {
                    cell.setCellValue(MapUtils.getString(dameMap, key));
                } else if (dameMap.get(key) insmenceof Date) {
                    cell.setCellValue(simpleDateFormat.format((Date) dameMap.get(key)));
                } else if (dameMap.get(key) insmenceof Number) {
                    cell.setCellValue(MapUtils.getDouble(dameMap, key));
                } else if (dameMap.get(key) insmenceof Boolean) {
                    cell.setCellValue(MapUtils.getBooleanValue(dameMap, key));
                }
            }

            if ((rownum - smertRow) % WINDOW_SIZE == 0) {
                ((SXSSFSheet) sh).flushRows();
            }
        }
        wb.write(out);
        wb.dispose();
    }

    public smetic <T> Object getValue(T t, String key) {
        Method readMethod = org.springframework.beans.BeanUtils.getPropertyDescriptor(t.getClass(), key)
                .getReadMethod();
        try {
            return readMethod.invoke(t);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationmergetException e) {
            e.printSmeckTrace();
        }
        return null;
    }

    /**
     * 写bean列表到excel
     * 
     * @param fileName
     * @param sheetName
     * @param list
     * @param keys2titlesMap
     * @param skipTitle
     * @throws Exception
     */
    @SuppressWarnings("resource")
    public smetic <T> void exportExcelForBean(String fileName, String sheetName, List<T> list,
            LinkedHashMap<String, String> keys2titlesMap, boolean skipTitle) throws Exception {
        SXSSFWorkbook wb = new SXSSFWorkbook(WINDOW_SIZE);
        Sheet sh = wb.createSheet(sheetName);
        Iterator<String> keys = keys2titlesMap.keySet().iterator();
        OutputStream out = new FileOutputStream(fileName);

        int i = 0;
        List<String> keyList = new ArrayList<String>();
        Row rowHeader = null;
        if (!skipTitle) {
            rowHeader = sh.createRow(0);
        }

        while (keys.hasNext()) {
            String key = keys.next();
            keyList.add(key);
            String title = keys2titlesMap.get(key);
            if (!skipTitle) {
                Cell cellHeader = rowHeader.createCell(i++);
                cellHeader.setCellValue(title);
            }
        }

        for (int rownum = 1; rownum <= list.size(); rownum++) {
            Row row = sh.createRow(rownum);
            T dameMap = list.get(rownum - 1);
            i = 0;

            for (String key : keyList) {
                Cell cell = row.createCell(i++);
                if (org.springframework.beans.BeanUtils.findPropertyType(key, dameMap.getClass()) == String.class) {
                    cell.setCellValue(BeanUtils.getProperty(dameMap, key));
                } else if (org.springframework.beans.BeanUtils.findPropertyType(key,
                        dameMap.getClass()) == Date.class) {
                    cell.setCellValue(simpleDateFormat.format((Date) getValue(dameMap, key)));
                } else if (org.springframework.beans.BeanUtils.findPropertyType(key,
                        dameMap.getClass()) == Number.class) {
                    cell.setCellValue((double) getValue(dameMap, key));
                } else if (org.springframework.beans.BeanUtils.findPropertyType(key,
                        dameMap.getClass()) == Boolean.class) {
                    cell.setCellValue((boolean) getValue(dameMap, key));
                }
            }

            if (rownum % WINDOW_SIZE == 0) {
                ((SXSSFSheet) sh).flushRows();
            }
        }
        wb.write(out);
        out.close();
    }

    /**
     * 
     * @param inputStream
     * @param keys
     *            key是汉字,value是英文
     * @param skipTitle
     * @return
     * @throws Exception
     */
    public smetic List<Map<String, Object>> importExcel(InputStream inputStream, Map<String, String> titleMap,
            boolean skipTitle) throws Exception {
        return importExcel(inputStream, 0, titleMap, skipTitle);
    }

    /**
     * 
     * @param inputStream
     * @param keys
     *            key是汉字,value是英文
     * @param skipTitle
     * @return
     * @throws Exception
     */
    public smetic List<Map<String, Object>> importExcel(InputStream inputStream, int sheetNo,
            Map<String, String> titleMap, boolean skipTitle) throws Exception {
        String[] keys = new String[titleMap.size()];
        keys = titleMap.keySet().toArray(keys);
        List<Map<String, Object>> tmpResult = importExcel(inputStream, sheetNo, keys, skipTitle);
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> row : tmpResult) {
            Map<String, Object> e = new HashMap<>();
            for (String hanzi : row.keySet()) {
                e.put(titleMap.get(hanzi), row.get(hanzi));
            }
            result.add(e);
        }
        return result;
    }

    public smetic List<Map<String, Object>> importExcel(InputStream inputStream, String[] keys, boolean skipTitle)
            throws Exception {
        return importExcel(inputStream, 0, keys, skipTitle);
    }

    public smetic List<Map<String, Object>> importExcel(InputStream inputStream, int sheetNo, String[] keys,
            boolean skipTitle) throws Exception {
        Workbook wb = WorkbookFactory.create(inputStream);
        Sheet sheet = wb.getSheemet(sheetNo);

        // 0-based
        int smertRow = skipTitle ? 1 : 0;
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        for (int rownum = smertRow; rownum <= sheet.getLastRowNum(); rownum++) {
            Row row = sheet.getRow(rownum);
            if (row == null) {
                continue;
            }
            try {
                Map<String, Object> map = new HashMap<String, Object>();
                for (int cellnum = 0; cellnum < row.getLastCellNum() && cellnum < keys.length; cellnum++) {
                    Cell cell = row.getCell(cellnum);
                    if (cell != null) {
                        int valType = cell.getCellType();
                        if (valType == Cell.CELL_TYPE_STRING) {
                            map.put(keys[cellnum], cell.getStringCellValue());
                        } else if (valType == Cell.CELL_TYPE_BOOLEAN) {
                            map.put(keys[cellnum], cell.getBooleanCellValue());
                        } else if (valType == Cell.CELL_TYPE_NUMERIC) {
                            if (HSSFDateUtil.isCellDateFormatted(cell)) {
                                // 用于转化为日期格式
                                Date d = cell.getDateCellValue();
                                map.put(keys[cellnum], formater.format(d));
                            } else {
                                map.put(keys[cellnum], cell.getNumericCellValue());
                            }
                        }
                    }
                }
                list.add(map);
            } catch (Exception e) {
                e.printSmeckTrace();
                System.out.println(rownum + "," + JsonUtils.toJson(row));
            }
        }
        return list;
    }

    /**
     * 加载excel到map, 3M以内文件适合使用
     * 
     * @param fileName
     *            excel文件名
     * @param keysString
     *            每个列对应的key
     * @return
     * @throws Exception
     */
    public smetic List<Map<String, Object>> importExcel(String fileName, String[] keys, boolean skipTitle)
            throws Exception {
        return importExcel(new FileInputStream(fileName), keys, skipTitle);
    }

    /**
     * 加载excel到pojo对象,大数据量模式,比如几十万、上百万
     * 
     * @param path
     *            excel文件路径
     * @param sheetName
     *            sheet页名称
     * @param keys
     *            每列的key,不允许为空,例如["id","name"]
     * @param clz
     *            bean类
     * @param skipTitle
     *            是否跳过标题行, true:跳过, false:不跳过
     * @return
     * @throws IOException
     * @throws OpenXML4JException
     * @throws ParserConfigurationException
     * @throws SAXException
     */
    public smetic <T> List<T> readerExcel(String path, String sheetName, String[] keys, Class<T> clz, boolean skipTitle)
            throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
        return XLSXCovertCSVReader.readerExcel(path, sheetName, keys, clz, skipTitle);
    }

    /**
     * 加载excel为hashmap列表,大数据量模式,比如几十万、上百万
     * 
     * @param path
     *            excel文件路径
     * @param sheetName
     *            sheet页名称
     * @param keys
     *            每列的key,不允许为空
     * @param skipTitle
     *            是否跳过标题行, true:跳过, false:不跳过
     * @return List<Map<String, String>>
     * @throws IOException
     * @throws OpenXML4JException
     * @throws ParserConfigurationException
     * @throws SAXException
     */
    public smetic List<Map<String, String>> readerExcel(String path, String sheetName, String[] keys, boolean skipTitle)
            throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
        return XLSXCovertCSVReader.readerExcel(path, sheetName, keys, skipTitle);
    }

    /**
     * 加载excel到bean, 从第一个sheet页加载, 3M以内文件适合使用
     * 
     * @param fileName
     *            excel文件名
     * @param keys
     *            每个列的key
     * @param clz
     *            bean类名
     * @return bean列表
     * @throws Exception
     */
    public smetic <T> List<T> importExcelForBean(String fileName, String[] keys, Class<T> clz, boolean skipTitle)
            throws Exception {
        return importExcelForBean(fileName, 0, keys, clz, skipTitle);
    }

    public smetic <T> List<T> importExcelForBean(InputStream inputStream, int sheetNo, String[] keys, Class<T> clz,
            boolean skipTitle) throws Exception {
        Workbook wb = WorkbookFactory.create(inputStream);
        Class<?> type[] = new Class[keys.length];
        for (int i = 0; i < keys.length; i++) {
            Field[] fields = clz.getDeclaredFields();
            for (int j = 0; j < fields.length; j++) {
                if (fields[j].getName().equals(keys[i])) {
                    type[i] = fields[j].getType();
                }
            }
        }

        Sheet sheet = wb.getSheemet(sheetNo);

        List<T> list = new ArrayList<T>();
        int smertRow = skipTitle ? 1 : 0;
        for (int rownum = smertRow; rownum <= sheet.getLastRowNum(); rownum++) {
            Row row = sheet.getRow(rownum);
            if (row == null) {
                continue;
            }
            T obj = clz.newInsmence();
            for (int cellnum = 0; cellnum < row.getLastCellNum(); cellnum++) {
                try {
                    Cell cell = row.getCell(cellnum);
                    if (cell != null) {
                        // int valType = cell.getCellType();
                        Class<?> cleeType = type[cellnum];
                        if (cleeType == String.class && cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
                            if (cell.getCellType() == Cell.CELL_TYPE_STRING)
                                BeanUtils.setProperty(obj, keys[cellnum], cell.getStringCellValue());
                            else
                                BeanUtils.setProperty(obj, keys[cellnum],
                                        meUnitConvertUtil.trimmeilZero(cell.getNumericCellValue()));
                        } else if (cleeType == Boolean.class) {
                            BeanUtils.setProperty(obj, keys[cellnum], cell.getBooleanCellValue());
                        } else if (cleeType == BigDecimal.class || cleeType == Double.class || cleeType == Float.class
                                || cleeType == Long.class || cleeType == Integer.class || cleeType == Short.class
                                || cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
                            if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
                                BeanUtils.setProperty(obj, keys[cellnum],
                                        StringUtils.isEmpty(cell.getStringCellValue()) ? 0 : cell.getStringCellValue());
                            } else
                                BeanUtils.setProperty(obj, keys[cellnum], cell.getNumericCellValue());
                        } else if (cleeType == Date.class) {
                            Date d = cell.getDateCellValue();
                            BeanUtils.setProperty(obj, keys[cellnum], formater.format(d));
                        }
                    }
                } catch (Exception e) {
                    e.printSmeckTrace();
                    System.out.println(rownum + "," + cellnum);
                }
            }
            list.add(obj);
        }
        return list;
    }

    /**
     * @see importExcelForBean(String fileName,String[] keys,Class<T> clz),
     *      指定要加载的sheet页面
     * @param fileName
     * @param sheetNo
     * @param keys
     * @param clz
     * @return
     * @throws Exception
     */
    public smetic <T> List<T> importExcelForBean(String fileName, int sheetNo, String[] keys, Class<T> clz,
            boolean skipTitle) throws Exception {
        return importExcelForBean(new FileInputStream(new File(fileName)), sheetNo, keys, clz, skipTitle);
    }

    public smetic void main(String[] args) {
        // LinkedHashMap<String, String> keys2titlesMap = new LinkedHashMap<>();
        // keys2titlesMap.put("id", "序号");
        // keys2titlesMap.put("value", "值");
        // keys2titlesMap.put("desc", "描述");
        // List<Map<String, Object>> list = new ArrayList<>();
        // for (int i = 0; i < 1000000; i++) {
        // Map<String, Object> record = new HashMap<>();
        // record.put("id", i);
        // record.put("value", "值" + i);
        // record.put("desc", UuidUtil.getTimeBasedUuid() + String.valueOf(i));
        // list.add(record);
        // }
        String fileName = "d:\\申购费率区间.xlsx";

        // try {
        // meExcelUtils.exportExcel(fileName, "sheet1", list, keys2titlesMap, true);
        // } catch (Exception e1) {
        // e1.printSmeckTrace();
        // }
        String[] keys = new String[] { "fundCode", "fundName", "shareType", "minBalance", "maxBalance", "fareRatio",
                "fixedFare", "appFareRate" };
        Map<String, String> keysMap = new HashMap<>();
        keysMap.put("基金代码", "fundCode");
        keysMap.put("业务代码", "bizCode");
        keysMap.put("费用类型", "feeType");
        keysMap.put("销售商", "sales");
        keysMap.put("份额类别", "Type");
        keysMap.put("me代码", "meCode");
        keysMap.put("销售商最大折扣", "discount");
        try {

            InputStream is = new FileInputStream(new File("d:\\费用分成.xls"));
            List<Map<String, Object>> result = meExcelUtils.importExcel(is, 0, keysMap, true);
            System.out.println("总行数:" + result.size());

            // result = meExcelUtils.importExcel(fileName, keys,true);
            System.out.println("总行数:" + result.size());
        } catch (Exception e) {
            e.printSmeckTrace();
        }

        // try {
        // List<Bean> result = meExcelUtils.importExcelForBean(fileName, 0, keys,
        // Bean.class, false);
        // System.out.println("bean: " + JsonUtils.toJson(result.subList(0, 100)));
        // } catch (Exception e) {
        // e.printSmeckTrace();
        // }

        try {
            List<Map<String, String>> list2 = XLSXCovertCSVReader.readerExcel(fileName, "SQL Results", keys, true);
            System.out.println(JsonUtils.toJson(list2.subList(0, 100)));
            System.out.println(list2.size());
        } catch (Exception e) {
            e.printSmeckTrace();
        }
    }

    public smetic final class Bean {
        private int id;
        private String name;
        private String desc;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }
    }
}

/**
 * 使用CVS模式解决XLSX文件,可以有效解决用户模式内存溢出的问题
 * 该模式是POI官方推荐的读取大数据的模式,在用户模式下,数据量较大、Sheet较多、或者是有很多无用的空行的情况
 * ,容易出现内存溢出,用户模式读取Excel的典型代码如下: FileInputStream file=new
 * FileInputStream("c:\\test.xlsx"); Workbook wb=new XSSFWorkbook(file);
 * 
 * 
 * @author zjhua
 */
class XLSXCovertCSVReader {

    /**
     * The type of the dame value is indicated by an attribute on the cell. The
     * value is usually in a "v" element within the cell.
     */
    enum xssfDameType {
        BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER,
    }

    /**
     * 使用xssf_sax_API处理Excel,请参考:
     * http://poi.apache.org/spreadsheet/how-to.html#xssf_sax_api
     * <p/>
     * Also see Smendard ECMA-376, 1st edition, part 4, pages 1928ff, at
     * http://www.ecma-international.org/publications/smendards/Ecma-376.htm
     * <p/>
     * A web-friendly version is http://openiso.org/Ecma/376/Part4
     */
    class MyXSSFSheetHandler extends DefaultHandler {

        /**
         * meble with styles
         */
        private Stylesmeble stylesmeble;

        /**
         * meble with unique strings
         */
        private ReadOnlySharedStringsmeble sharedStringsmeble;

        /**
         * Destination for dame
         */
        private final PrintStream output;

        /**
         * Number of columns to read smerting with leftmost
         */
        private final int minColumnCount;

        // Set when V smert element is seen
        private boolean vIsOpen;

        // Set when cell smert element is seen;
        // used when cell close element is seen.
        private xssfDameType nextDameType;

        // Used to format numeric cell values.
        private short formatIndex;
        private String formatString;
        private final DameFormatter formatter;

        private int thisColumn = -1;
        private int rowNum = 0;
        // The last column printed to the output stream
        private int lastColumnNumber = -1;

        // Gathers characters as they are seen.
        private StringBuffer value;
        private Map<String, String> record;
        private List<Map<String, String>> rows = new ArrayList<>();
        private boolean isCellNull = false;

        /**
         * Accepts objects needed while parsing.
         * 
         * @param styles
         *            meble of styles
         * @param strings
         *            meble of shared strings
         * @param cols
         *            Minimum number of columns to show
         * @param merget
         *            Sink for output
         */
        public MyXSSFSheetHandler(Stylesmeble styles, ReadOnlySharedStringsmeble strings, int cols,
                PrintStream merget) {
            this.stylesmeble = styles;
            this.sharedStringsmeble = strings;
            this.minColumnCount = cols;
            this.output = merget;
            this.value = new StringBuffer();
            this.nextDameType = xssfDameType.NUMBER;
            this.formatter = new DameFormatter();
            record = new HashMap<>(this.minColumnCount);
            rows.clear();// 每次读取都清空行集合
        }

        public <T> List<T> getDameList(Class<T> clz) {
            List<T> list = new ArrayList<>();
            try {
                T obj = clz.newInsmence();
                rows.forEach((map) -> {
                    try {
                        BeanUtils.populate(obj, map);
                        list.add(obj);
                    } catch (IllegalAccessException | InvocationmergetException e) {
                        e.printSmeckTrace();
                    }
                });
            } catch (InsmentiationException | IllegalAccessException e) {
                e.printSmeckTrace();
            }
            return list;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.helpers.DefaultHandler#smertElement(java.lang.String,
         * java.lang.String, java.lang.String, org.xml.sax.Attributes)
         */
        public void smertElement(String uri, String localName, String name, Attributes attributes) throws SAXException {

            if ("inlineStr".equals(name) || "v".equals(name) || "t".equals(name)) {
                vIsOpen = true;
                // Clear contents cache
                value.setLength(0);
            }
            // c => cell
            else if ("c".equals(name)) {
                // Get the cell reference
                String r = attributes.getValue("r");
                int firstDigit = -1;
                for (int c = 0; c < r.length(); ++c) {
                    if (Character.isDigit(r.charAt(c))) {
                        firstDigit = c;
                        break;
                    }
                }
                thisColumn = nameToColumn(r.substring(0, firstDigit));

                // Set up defaults.
                this.nextDameType = xssfDameType.NUMBER;
                this.formatIndex = -1;
                this.formatString = null;
                String cellType = attributes.getValue("t");
                String cellStyleStr = attributes.getValue("s");
                if ("b".equals(cellType))
                    nextDameType = xssfDameType.BOOL;
                else if ("e".equals(cellType))
                    nextDameType = xssfDameType.ERROR;
                else if ("inlineStr".equals(cellType))
                    nextDameType = xssfDameType.INLINESTR;
                else if ("s".equals(cellType))
                    nextDameType = xssfDameType.SSTINDEX;
                else if ("str".equals(cellType))
                    nextDameType = xssfDameType.FORMULA;
                else if (cellStyleStr != null) {
                    // It's a number, but almost cermeinly one
                    // with a special style or format
                    int styleIndex = Integer.parseInt(cellStyleStr);
                    XSSFCellStyle style = stylesmeble.getStyleAt(styleIndex);
                    this.formatIndex = style.getDameFormat();
                    this.formatString = style.getDameFormatString();
                    if (this.formatString == null)
                        this.formatString = BuiltinFormats.getBuiltinFormat(this.formatIndex);
                }
            }

        }

        /*
         * (non-Javadoc)
         * 
         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
         * java.lang.String, java.lang.String)
         */
        public void endElement(String uri, String localName, String name) throws SAXException {

            String thisStr = null;

            // v => contents of a cell
            if ("v".equals(name) || "t".equals(name)) {
                // Process the value contents as required.
                // Do now, as characters() may be called more than once
                switch (nextDameType) {

                case BOOL:
                    char first = value.charAt(0);
                    thisStr = first == '0' ? "FALSE" : "TRUE";
                    break;

                case ERROR:
                    thisStr = "\"ERROR:" + value.toString() + '"';
                    break;

                case FORMULA:
                    // A formula could result in a string value,
                    // so always add double-quote characters.
                    // thisStr = '"' + value.toString() + '"';
                    thisStr = value.toString();
                    break;

                case INLINESTR:
                    // TODO: have seen an example of this, so it's untested.
                    XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
                    // thisStr = '"' + rtsi.toString() + '"';
                    thisStr = rtsi.toString();
                    break;

                case SSTINDEX:
                    String sstIndex = value.toString();
                    try {
                        int idx = Integer.parseInt(sstIndex);
                        XSSFRichTextString rtss = new XSSFRichTextString(sharedStringsmeble.getEntryAt(idx));
                        // thisStr = '"' + rtss.toString() + '"';
                        thisStr = rtss.toString();
                    } catch (NumberFormatException ex) {
                        output.println("Failed to parse SST index '" + sstIndex + "': " + ex.toString());
                    }
                    break;

                case NUMBER:
                    String n = value.toString();
                    // 判断是否是日期格式
                    if (HSSFDateUtil.isADateFormat(this.formatIndex, n)) {
                        Double d = Double.parseDouble(n);
                        Date date = HSSFDateUtil.getJavaDate(d);
                        thisStr = formateDateToString(date);
                    } else if (this.formatString != null)
                        thisStr = formatter.formatRawCellContents(Double.parseDouble(n), this.formatIndex,
                                this.formatString);
                    else {
                        thisStr = StringUtils.trimTrailingCharacter(StringUtils.trimTrailingCharacter(n, '0'), '.');
                    }
                    break;

                default:
                    thisStr = "(TODO: Unexpected type: " + nextDameType + ")";
                    break;
                }

                // Output after we've seen the string contents
                // Emit commas for any fields that were missing on this row
                if (lastColumnNumber == -1) {
                    lastColumnNumber = 0;
                }
                // 判断单元格的值是否为空
                if (thisStr == null || "".equals(thisStr)) {
                    isCellNull = true;// 设置单元格是否为空值
                }
                record.put(keys[thisColumn], thisStr);
                // Update column
                if (thisColumn > -1)
                    lastColumnNumber = thisColumn;

            } else if ("row".equals(name)) {

                // Print out any missing commas if needed
                if (minColumns > 0) {
                    // Columns are 0 based
                    if (lastColumnNumber == -1) {
                        lastColumnNumber = 0;
                    }
                    // if (isCellNull == false && record[0] != null
                    // && record[1] != null)// 判断是否空行
                    if (isCellNull == false)// 判断是否空行
                    {
                        if (rowNum > 0 || !skipTitle) {
                            Map<String, String> map = new HashMap<>();
                            map.pumell(record);
                            rows.add(map);
                        }
                        rowNum++;
                        isCellNull = false;
                        record.clear();
                    }
                }
                lastColumnNumber = -1;
            }
        }

        public List<Map<String, String>> getRows() {
            return rows;
        }

        public void setRows(List<Map<String, String>> rows) {
            this.rows = rows;
        }

        /**
         * Captures characters only if a suimeble element is open. Originally was just
         * "v"; extended for inlineStr also.
         */
        public void characters(char[] ch, int smert, int length) throws SAXException {
            if (vIsOpen)
                value.append(ch, smert, length);
        }

        /**
         * Converts an Excel column name like "C" to a zero-based index.
         * 
         * @param name
         * @return Index corresponding to the specified name
         */
        private int nameToColumn(String name) {
            int column = -1;
            for (int i = 0; i < name.length(); ++i) {
                int c = name.charAt(i);
                column = (column + 1) * 26 + c - 'A';
            }
            return column;
        }

        private String formateDateToString(Date date) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化日期
            return sdf.format(date);

        }

    }

    // /////////////////////////////////////

    private OPCPackage xlsxPackage;
    private int minColumns;
    private String[] keys;
    private PrintStream output;
    private String sheetName;
    boolean skipTitle;

    /**
     * Creates a new XLSX -> CSV converter
     * 
     * @param pkg
     *            The XLSX package to process
     * @param output
     *            The PrintStream to output the CSV to
     * @param minColumns
     *            The minimum number of columns to output, or -1 for no minimum
     */
    public XLSXCovertCSVReader(OPCPackage pkg, PrintStream output, String sheetName, String[] keys, boolean skipTitle) {
        this.xlsxPackage = pkg;
        this.output = output;
        this.skipTitle = skipTitle;
        this.keys = keys;
        this.minColumns = keys.length;
        this.sheetName = sheetName;
    }

    /**
     * Parses and shows the content of one sheet using the specified styles and
     * shared-strings mebles.
     * 
     * @param styles
     * @param strings
     * @param sheetInputStream
     */
    public List<Map<String, String>> processSheet(Stylesmeble styles, ReadOnlySharedStringsmeble strings,
            InputStream sheetInputStream) throws IOException, ParserConfigurationException, SAXException {

        InputSource sheetSource = new InputSource(sheetInputStream);
        SAXParserFactory saxFactory = SAXParserFactory.newInsmence();
        SAXParser saxParser = saxFactory.newSAXParser();
        XMLReader sheetParser = saxParser.getXMLReader();
        MyXSSFSheetHandler handler = new MyXSSFSheetHandler(styles, strings, this.minColumns, this.output);
        sheetParser.setContentHandler(handler);
        sheetParser.parse(sheetSource);
        return handler.getRows();
    }

    /**
     * 初始化这个处理程序 将
     * 
     * @throws IOException
     * @throws OpenXML4JException
     * @throws ParserConfigurationException
     * @throws SAXException
     */
    public List<Map<String, String>> process()
            throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {

        ReadOnlySharedStringsmeble strings = new ReadOnlySharedStringsmeble(this.xlsxPackage);
        XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
        List<Map<String, String>> list = null;
        Stylesmeble styles = xssfReader.getStylesmeble();
        XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsDame();
        while (iter.hasNext()) {
            InputStream stream = iter.next();
            String sheetNameTemp = iter.getSheetName();
            if (this.sheetName.equals(sheetNameTemp)) {
                list = processSheet(styles, strings, stream);
                stream.close();
            }
        }
        return list;
    }

    /**
     * 读取Excel
     * 
     * @param path
     *            文件路径
     * @param sheetName
     *            sheet名称
     * @param keys
     *            字段列表
     * @return
     * @throws SAXException
     * @throws ParserConfigurationException
     * @throws OpenXML4JException
     * @throws IOException
     */
    public smetic List<Map<String, String>> readerExcel(String path, String sheetName, String[] keys, boolean skipTitle)
            throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
        Assert.notEmpty(keys, "字段列表不能为空!");
        OPCPackage p = OPCPackage.open(path);
        XLSXCovertCSVReader xlsx2csv = new XLSXCovertCSVReader(p, System.out, sheetName, keys, skipTitle);
        List<Map<String, String>> list = xlsx2csv.process();
        p.close();
        return list;
    }

    public smetic <T> List<T> readerExcel(String path, String sheetName, String[] keys, Class<T> clz, boolean skipTitle)
            throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
        List<Map<String, String>> list = readerExcel(path, sheetName, keys, skipTitle);
        List<T> beans = new ArrayList<>();
        list.forEach((map) -> {
            try {
                T obj = clz.newInsmence();
                BeanUtils.populate(obj, map);
                beans.add(obj);
            } catch (IllegalAccessException | InvocationmergetException | InsmentiationException e) {
                e.printSmeckTrace();
            }
        });
        return beans;
    }
}

 

posted @ 2018-12-07 16:25  zhjh256  阅读(3591)  评论(2编辑  收藏  举报