SpringBoot + Vue 后台管理系统(七):POI读写操作

在最近项目中有使用了POI读取Excel文件数据,插入到数据库中的操作。

先了解一下POI是什么?

poi是Apache社区的一个开源项目,您可以使用Java读写Excel文件(本篇主要了解)。此外,您还可以使用Java读取和写入MS Word和MS PowerPoint文件。
具体参考文档:Apache POI;

引入依赖
    
    
        org.apache.poi
        poi
        4.0.0
      
    
        org.apache.poi
        poi-ooxml
        4.0.0
     

POI Excel操作(HSSF/XSSF)几种API?

1: 用户模型API: 大多数情况都可以使用。

通过创建Wordbook的实例来创建工作簿;
通过从现有的Workbook实例调用createSheet()创建表单Sheet实例;
通过从现有的Sheet实例调用createRow(rowNumber)来创建行Row实例;
通过从现有Row调用createCell(column,type)来创建单元格。
通过从Workbook实例调用write(outputStream)将其写到文件。注:需要手动关闭流。
示例:

写入
public static void main(String[] args) throws Exception {
    // 创建工作蒲
    Workbook workbook = new HSSFWorkbook();
    // 创建表单 -- 设置名字
    Sheet sheet = workbook.createSheet("POI测试写入表");
    
    // 创建3个单元格样式
    CellStyle cs = workbook.createCellStyle();
    CellStyle cs2 = workbook.createCellStyle();
    CellStyle cs3 = workbook.createCellStyle();
    DataFormat df = workbook.createDataFormat();
    // 创建2个字体对象
    Font f = workbook.createFont();
    Font f2 = workbook.createFont();

    // 设置字体1到12点类型
    f.setFontHeightInPoints((short)12);
    // 把它弄成蓝色
    f.setColor((short)0xc);
    // 加粗 
    f.setBold(true);;

    // 将字体2设置为10磅类型
    f2.setFontHeightInPoints((short)10);
    // 把它弄成红色
    f2.setColor((short)Font.COLOR_RED);
    // 加粗
    f2.setBold(true);

    f2.setStrikeout(true);

    // 设置细胞stlye
    cs.setFont(f);
    // 设置单元格格式 
    cs.setDataFormat(df.getFormat( "#,## 0.0"));

    // 设置一个细边框
    cs2.setBorderBottom(BorderStyle.THIN);
    // 填充w fg填充颜色 
    cs2.setFillPattern(FillPatternType.SOLID_FOREGROUND);;
    // 将单元格格式设置为文本,请参阅DataFormat以获取完整列表
    cs2.setDataFormat(HSSFDataFormat.getBuiltinFormat("文本"));
    // 设置字体
    cs2.setFont(f2);

    // 创建一个包含30行(0-29)的工作表
    // 一行
    Row row = null;
    // 一个单元格
    Cell cell = null;
    int rownum;
    for(rownum =(short)0; rownum <30; rownum ++) {
        // 创建一行
        row = sheet.createRow(rownum);
        // 在每一行上
        if((rownum%2)== 0) {
            // 使行高更大(以缇为单位的1/20)
            row.setHeight((short)0x249);
        } 
        // r.setRowNum((简称)rownum);
        // 创建10个单元格(0-9)(+ = 2稍后变得明显
        for(short cellnum =(short)0; cellnum <10; cellnum += 2) {
            // 创建一个数字单元格
            cell = row.createCell(cellnum);
            // 做一些愚蠢的数学来证明小数
            cell.setCellValue(rownum * 10000 + cellnum +(((double)rownum / 1000) +((double)cellnum / 10000)));

            // 创建一个字符串单元格(请参阅为什么+ = 2)
            cell = row.createCell((short)(cellnum + 1));
            
            // 在每一行上
            if((rownum%2)== 0) {
                //将此单元格设置为我们定义的第一个单元格样式
                cell.setCellStyle(cs);
                //将单元格的字符串值设置为“Test”
                cell.setCellValue("Test");
            }
            else {
                cell.setCellStyle(cs2);
                //将单元格的字符串值设置为“\ u0422 \ u0435 \ u0441 \ u0442”
                cell.setCellValue(" u0422 u0435 u0441 u0442");
            }
            //使这个专栏更宽一些
            sheet.setColumnWidth((short)(cellnum + 1), (short)((50 * 8)/((double)1/20)));
        }
    }

    //使用BLANKS在底部的行上绘制一个粗黑色边框
    //前进2行
    rownum ++;
    rownum ++;

    row = sheet.createRow(rownum);

    //将第三个样式定义为默认样式
    //除了底部有一个粗黑边框
    cs3.setBorderBottom(BorderStyle.THICK);

    //创建50个单元格
    for(short cellnum =(short)0; cellnum <50; cellnum ++) {
        //创建一个空白类型单元格(没有值)
        cell = row.createCell(cellnum);
        //将其设置为粗黑边框样式
        cell.setCellStyle(cs3);
    }

    //结束绘制厚黑色边框

    // 测试文件
    String filePath = "D://POI-test/workbook.xls";
    // 创建一个新文件
    FileOutputStream out = new FileOutputStream(filePath);
    // 将工作簿写入输出流
    // 关闭我们的文件 
    workbook.write(out);
    out.close();
    // 关闭工作蒲
    workbook.close();
}
读取
public static void read() throws Exception {
    // 测试文件
    String filePath = "D://POI-test/workbook.xls";
    // 输入流
    FileInputStream inp = new FileInputStream(filePath);
    
    Workbook workbook = new HSSFWorkbook(inp);
    Sheet sheet = workbook.getSheetAt(0);
    // 迭代所有的行 
    Iterator rowIterator = sheet.rowIterator();
    // 数据
    while (rowIterator.hasNext()) {
        Row row = rowIterator.next();
        Iterator cellIterator = row.cellIterator();
        System.out.print("第" + row.getRowNum() + "行:  =》 { ");
        while (cellIterator.hasNext()) {
            Cell cell = cellIterator.next();
            CellType cellType = cell.getCellType();
            Object cellValue;
            switch (cellType) {
            case _NONE:
                cellValue = cell.getStringCellValue();
                break;
            case NUMERIC:
                cellValue = cell.getNumericCellValue();
                break;
            case STRING:
                cellValue = cell.getStringCellValue();
                break;
            case FORMULA:
                cellValue = cell.getDateCellValue();
                break;
            case BLANK:
                cellValue = "";
                break;
            case BOOLEAN:
                cellValue = cell.getBooleanCellValue();
                break;
            case ERROR:
                cellValue = cell.getErrorCellValue();
                break;
            default:
                cellValue = null;
                break;
            }
            int columnIndex = cell.getColumnIndex();
            int rowIndex = cell.getRowIndex();
            System.out.print("(行:列 -" + rowIndex + ":" + columnIndex);
            
            System.out.print("==" + cellValue + ")");
        }
        System.out.println();
    }
    
    inp.close();
    workbook.close(); 
}
2:事件驱动API: 仅限于读取(HSSF)

相对于用户模型读取,事件驱动API的优势在于占用内存较小。
示例:

/**
 * 这个例子展示了如何使用事件API来读取文件。
 */
public class EventExample implements HSSFListener {
    private SSTRecord sstrec;

    /**
     * 此方法侦听传入的记录并根据需要处理它们。
     * @param record    阅读时发现的记录
     */
    public void processRecord(Record record) {
        // record.getSid() 返回此记录的非静态id版本
        switch (record.getSid()) {
            // BOFRecord 既可以表示工作表的开头,也可以表示工作簿的开头
            case BOFRecord.sid: // 文件的開始
                BOFRecord bof = (BOFRecord) record;
                if (bof.getType() == bof.TYPE_WORKBOOK) {
                    System.out.println("Encountered workbook");
                    // 分配给类级成员
                } else if (bof.getType() == bof.TYPE_WORKSHEET) {
                    System.out.println("Encountered sheet reference");
                }
                break;
            case BoundSheetRecord.sid: // 工作蒲纪录
                BoundSheetRecord bsr = (BoundSheetRecord) record;
                System.out.println("工作蒲名稱: " + bsr.getSheetname());
                break;
            case RowRecord.sid: // 行记录 
                RowRecord rowrec = (RowRecord) record;
                System.out.println("找到一行, 第一列: " + rowrec.getFirstCol() + " 最后一列: " + rowrec.getLastCol());
                break;
            case NumberRecord.sid: // 包含一个数值单元格值
                NumberRecord numrec = (NumberRecord) record;
                System.out.println("单元格: " + numrec.getValue() + " 在 "+ numrec.getRow() + "行 " + numrec.getColumn() + " 列 ");
                break;
                // SSTRecords存储Excel中使用的惟一字符串数组
            case SSTRecord.sid: // 静态字符串 
                sstrec = (SSTRecord) record;
                for (int k = 0; k < sstrec.getNumUniqueStrings(); k++) {
                    System.out.println("字符串表值 " + k + " = " + sstrec.getString(k));
                }
                break;
            case LabelSSTRecord.sid: // 引用共享字符串表中的字符串
                LabelSSTRecord lrec = (LabelSSTRecord) record;
                System.out.println("找到带值的字符串单元格:" + sstrec.getString(lrec.getSSTIndex()));
                break;
        }
    }

    /**
     * Read an excel file and spit out what we find.
     *
     * @param args      Expect one argument that is the file to read.
     * @throws IOException  When there is an error processing the file.
     */
    public static void main(String[] args) throws IOException {
        // 测试文件
        String filePath = "D://POI-test/workbook.xls";
        // 使用指定的输入文件创建一个新的文件输入流
        FileInputStream fin = new FileInputStream(filePath);
        // 创建一个新的org.apache.poi.poifs.filesystem.Filesystem
        POIFSFileSystem poifs = new POIFSFileSystem(fin);
        // 在InputStream中获取工作簿(excel部分)流
        InputStream din = poifs.createDocumentInputStream("Workbook");
        // 构造HSSFRequest 对象
        HSSFRequest req = new HSSFRequest();
        // 使用上面所示的侦听器惰性侦听所有记录
        req.addListenerForAllRecords(new EventExample());
        // 创建事件工厂
        HSSFEventFactory factory = new HSSFEventFactory();
        // 根据文档输入流处理事件
        factory.processEvents(req, din);
        // 处理完所有事件后,关闭文件输入流
        fin.close(); 
        din.close();
        poifs.close();
        // 完成
        System.out.println("done.");
    }
}

3:事件API扩展(仅限HSSF)

监听器将被调用额外的虚拟记录。这些虚拟记录应提醒你文件中不存在的记录(例如尚未编辑的单元格),并允许您处理这些记录。

  • org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord
    在行记录阶段(通常在单元格记录之前发生)期间调用的。
  • org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord
    在单元格记录阶段调用的。
  • org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord
    在给定行的最后一个单元格之后调用的。
    示例:
/**
 *  一个XLS -> CSV处理器,它使用了MissingRecordAware
 *  EventModel代码,以确保它输出所有列和行。 
 */
public class XLS2CSVmra implements HSSFListener {
    private int minColumns;
    private POIFSFileSystem fs;
    private PrintStream output;

    private int lastRowNumber;
    private int lastColumnNumber;

    /** 应该输出公式,还是它的值? */
    private boolean outputFormulaValues = true;

    /** 解析公式 */
    private SheetRecordCollectingListener workbookBuildingListener;
    private HSSFWorkbook stubWorkbook;

    // 在处理过程中收集的记录
    private SSTRecord sstRecord;
    private FormatTrackingHSSFListener formatListener;
    
    /** sheet信息 */
    private int sheetIndex = -1;
    private BoundSheetRecord[] orderedBSRs;
    private List boundSheetRecords = new ArrayList<>();

    // 用于处理带字符串结果的公式
    private int nextRow;
    private int nextColumn;
    private boolean outputNextStringRecord;

    /**
     * 创建一个新的XLS -> CSV转换器
     * @param fs 要处理的POIFSFileSystem
     * @param output 输出CSV到的PrintStream
     * @param minColumns 要输出的列数的最小值,或-1表示没有最小值
     */
    public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
        this.fs = fs;
        this.output = output;
        this.minColumns = minColumns;
    }

    /**
     * 创建一个新的XLS -> CSV转换器
     * @param filename 要处理的文件
     * @param minColumns 要输出的列数的最小值,或-1表示没有最小值
     */
    public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
        this(
                new POIFSFileSystem(new FileInputStream(filename)),
                System.out, minColumns
        );
    }

    /**
     * 启动对XLS文件到CSV的处理
     */
    public void process() throws IOException {
        MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
        formatListener = new FormatTrackingHSSFListener(listener);

        HSSFEventFactory factory = new HSSFEventFactory();
        HSSFRequest request = new HSSFRequest();

        if(outputFormulaValues) {
            request.addListenerForAllRecords(formatListener);
        } else {
            workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
            request.addListenerForAllRecords(workbookBuildingListener);
        }

        factory.processWorkbookEvents(request, fs);
    }

    /**
     * 主HSSFListener方法,处理事件,并在处理文件时输出CSV。
     */
    @Override
    public void processRecord(Record record) {
        int thisRow = -1;
        int thisColumn = -1;
        String thisStr = null;

        switch (record.getSid())
        {
        case BoundSheetRecord.sid:
            boundSheetRecords.add((BoundSheetRecord)record);
            break;
        case BOFRecord.sid:
            BOFRecord br = (BOFRecord)record;
            if(br.getType() == BOFRecord.TYPE_WORKSHEET) {
                // 创建子工作簿
                if(workbookBuildingListener != null && stubWorkbook == null) {
                    stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
                }
                
                // 输出工作表名称
                // 其工作原理是,根据BSRs的BOFRecords的位置对其进行排序,然后知道我们按照字节偏移顺序处理BOFRecords
                sheetIndex++;
                if(orderedBSRs == null) {
                    orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords);
                }
                output.println();
                output.println( 
                        orderedBSRs[sheetIndex].getSheetname() +
                        " [" + (sheetIndex+1) + "]:"
                );
            }
            break;

        case SSTRecord.sid:
            sstRecord = (SSTRecord) record;
            break;

        case BlankRecord.sid:
            BlankRecord brec = (BlankRecord) record;

            thisRow = brec.getRow();
            thisColumn = brec.getColumn();
            thisStr = "";
            break;
        case BoolErrRecord.sid:
            BoolErrRecord berec = (BoolErrRecord) record;

            thisRow = berec.getRow();
            thisColumn = berec.getColumn();
            thisStr = "";
            break;

        case FormulaRecord.sid:
            FormulaRecord frec = (FormulaRecord) record;

            thisRow = frec.getRow();
            thisColumn = frec.getColumn();

            if(outputFormulaValues) {
                if(Double.isNaN( frec.getValue() )) {
                    outputNextStringRecord = true;
                    nextRow = frec.getRow();
                    nextColumn = frec.getColumn();
                } else {
                    thisStr = formatListener.formatNumberDateCell(frec);
                }
            } else {
                thisStr = '"' +
                    HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
            }
            break;
        case StringRecord.sid:
            if(outputNextStringRecord) {
                StringRecord srec = (StringRecord)record;
                thisStr = srec.getString();
                thisRow = nextRow;
                thisColumn = nextColumn;
                outputNextStringRecord = false;
            }
            break;

        case LabelRecord.sid:
            LabelRecord lrec = (LabelRecord) record;

            thisRow = lrec.getRow();
            thisColumn = lrec.getColumn();
            thisStr = '"' + lrec.getValue() + '"';
            break;
        case LabelSSTRecord.sid:
            LabelSSTRecord lsrec = (LabelSSTRecord) record;

            thisRow = lsrec.getRow();
            thisColumn = lsrec.getColumn();
            if(sstRecord == null) {
                thisStr = '"' + "(No SST Record, can't identify string)" + '"';
            } else {
                thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
            }
            break;
        case NoteRecord.sid:
            NoteRecord nrec = (NoteRecord) record;

            thisRow = nrec.getRow();
            thisColumn = nrec.getColumn();
            // TODO: Find object to match nrec.getShapeId()
            thisStr = '"' + "(TODO)" + '"';
            break;
        case NumberRecord.sid:
            NumberRecord numrec = (NumberRecord) record;

            thisRow = numrec.getRow();
            thisColumn = numrec.getColumn();

            // Format
            thisStr = formatListener.formatNumberDateCell(numrec);
            break;
        case RKRecord.sid:
            RKRecord rkrec = (RKRecord) record;

            thisRow = rkrec.getRow();
            thisColumn = rkrec.getColumn();
            thisStr = '"' + "(TODO)" + '"';
            break;
        default:
            break;
        }

        // 处理新行
        if(thisRow != -1 && thisRow != lastRowNumber) {
            lastColumnNumber = -1;
        }

        // 处理缺失列
        if(record instanceof MissingCellDummyRecord) {
            MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
            thisRow = mc.getRow();
            thisColumn = mc.getColumn();
            thisStr = "";
        }

        // 如果我们有东西要打印出来,就打印出来
        if(thisStr != null) {
            if(thisColumn > 0) {
                output.print(',');
            }
            output.print(thisStr);
        }

        // 更新列和行计数
        if(thisRow > -1)
            lastRowNumber = thisRow;
        if(thisColumn > -1)
            lastColumnNumber = thisColumn;

        // 行结束端
        if(record instanceof LastCellOfRowDummyRecord) {
            // 如果需要,打印出任何缺少的逗号
            if(minColumns > 0) {
                // 列是基于0的
                if(lastColumnNumber == -1) { lastColumnNumber = 0; }
                for(int i=lastColumnNumber; i<(minColumns); i++) {
                    output.print(',');
                }
            }

            // 我们到了新一行
            lastColumnNumber = -1;

            // 结束行
            output.println();
        }
    }

    public static void main(String[] args) throws Exception {
        String filePath = "D://POI-test/workbook.xls";
        XLS2CSVmra xls2csv = new XLS2CSVmra(filePath, 10);
        xls2csv.process();
    }
}
4:XSSF和SAX(事件API)

使用内存较小读取大文件是选择方案,
示例

/**
 * @Author Bertram.wang
 * @Date 2019年6月25日 下午5:03:11
 * @sign POIExcelUtils.java
 */
public class POIExcelUtils {
    private XSSFReader xssfReader;
    //获取一行时最小数组长度
    private final int minColumnCount;
    private int currentRow = 0;
    private int sheetIndex = -1;
    private String[] record;
    private int thisColumnIndex = -1;
    // 日期标志
    private boolean dateFlag;
    // 数字标志
    private boolean numberFlag;
    private boolean isTElement;
    private RowReader rowReader;

    public void setRowReader(RowReader rowReader) {
        this.rowReader = rowReader;
    }

    /**
     * 构造方法
     */
    public POIExcelUtils (String filename,int minCols) throws Exception {
        if (StringUtils.isEmpty(filename)) throw new Exception("文件名不能空");
        this.minColumnCount = minCols;
        record = new String[this.minColumnCount];
        OPCPackage pkg = OPCPackage.open(filename);
        init(pkg);

    }
    public POIExcelUtils (InputStream is,int minCols) throws Exception {
        if (null == is) throw new Exception("文件不能空");
        this.minColumnCount = minCols;
        record = new String[this.minColumnCount];
        OPCPackage pkg = OPCPackage.open(is);
        init(pkg);
    }
    private void init (OPCPackage pkg) throws IOException, OpenXML4JException {
        xssfReader = new XSSFReader(pkg);
    }

    /**
     * 获取sheet
     * @throws Exception
     */
    public void process() throws Exception {
        SharedStringsTable sst = xssfReader.getSharedStringsTable();
        XMLReader parser = fetchSheetParser(sst);
        Iterator it = xssfReader.getSheetsData();
        while (it.hasNext()) {
            sheetIndex++;
            InputStream sheet = it.next();
            InputSource sheetSource = new InputSource(sheet);
            parser.parse(sheetSource);
            sheet.close();
        }
    }

    /**
     * 加载sax 解析器 
     * @param sst
     * @return
     * @throws SAXException
     */
    private XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
        XMLReader parser = XMLReaderFactory.createXMLReader("com.sun.org.apache.xerces.internal.parsers.SAXParser");
        ContentHandler handler = new PagingHandler(sst);
        parser.setContentHandler(handler);
        return parser;
    }
    
    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;
    }
    /**
     * See org.xml.sax.helpers.DefaultHandler javadocs
     */
    private class PagingHandler extends DefaultHandler {
        private SharedStringsTable sst;
        private String lastContents;
        private boolean nextIsString;
        private String index = null; 
        
        private PagingHandler(SharedStringsTable sst) {
            this.sst = sst;
        }

        /**
         * 开始元素
         */
        @Override
        public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
            if (name.equals("c")) {
                index = attributes.getValue("r");
                int firstDigit = -1;
                for (int c = 0; c < index.length(); ++c) {
                    if (Character.isDigit(index.charAt(c))) {
                        firstDigit = c;
                        break;
                    }
                }
                thisColumnIndex = nameToColumn(index.substring(0, firstDigit));
                
                // 判断是否是新的一行
                if (Pattern.compile("^A[0-9]+$").matcher(index).find()) {
                    currentRow++;
                }
                String cellType = attributes.getValue("t");
                if (cellType != null && cellType.equals("s")) {
                    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 = "";
        }

        /**
         * 获取value
         */
        @Override
        public void endElement(String uri, String localName, String name) throws SAXException {
            if (nextIsString) {
                int idx = Integer.parseInt(lastContents);
                lastContents = sst.getItemAt(idx).getString();
                nextIsString = false;
            }
            // t元素也包含字符串
            if (isTElement) {
                String value = lastContents.trim();
                record[thisColumnIndex] = value;
                isTElement = false;
                // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
                // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
            } else if ("v".equals(name)) {
                String value = lastContents.trim();
                value = value.equals("") ? " " : value;
                // 日期格式处理
                if (dateFlag) {
                    try {
                        Date date = HSSFDateUtil.getJavaDate(Double.valueOf(value));
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        value = dateFormat.format(date);
                    } catch (NumberFormatException e) {
                    }
                }
                // 数字类型处理
                if (numberFlag) {
                    try {
                        BigDecimal bd = new BigDecimal(value);
                        value = bd.setScale(3, BigDecimal.ROUND_UP).toString();
                    } catch (Exception e) {
                    }
                }
                record[thisColumnIndex] = value;
                
            } else {
                if (name.equals("row")) {
                    if (minColumnCount > 0) {
                        rowReader.getRows(sheetIndex, currentRow, record.clone());
                        for (int i = 0; i < record.length; i++) {
                            record[i] = null;
                        }
                    }
                }
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            lastContents += new String(ch, start, length);
        }

    }
}

行处理

/** 
 * @Author Bertram.wang
 * @Date 2019年6月25日 下午5:14:40
 * @sign RowReader.java Excel行处理
 */
public class RowReader{ 
    
    private static final Logger log = LoggerFactory.getLogger(RowReader.class);

    private Integer curRow=-1;
    private List contents = new ArrayList<>();
    private Map map = new LinkedHashMap<>();
    private ObjectMapper objMap = new ObjectMapper();
    private Class clazz;
    
    
    
    private RowReader() {
        super();
    }

    public static RowReader create(Class clazz) {
        RowReader rowReader = new RowReader();
        rowReader.clazz = clazz;
        return rowReader;
    }

    public void getRows(int sheetIndex, int curRow, String[] rows) {
        if(this.curRow!=curRow) {
            if (curRow == 1) {
                for (String cell : rows) {
                    if (cell != null) {
                        map.put(cell, "");
                    }
                }
            }else {
                Set keySet = map.keySet();
                Object[] array = keySet.toArray();
                for (int i = 0; i < array.length; i++) {
                    map.put(array[i].toString(), rows[i]);
                }
                Object readValue;
                try {
                    readValue = objMap.readValue(objMap.writeValueAsString(map), clazz);
                } catch (Exception e) {
                    log.error(e.getMessage());
                    readValue = null;
                }
                contents.add(readValue);
            }
            this.curRow=curRow;
        }
    }
    
    public List getContents(){
        return this.contents;
    }
}

主方法测试:

@org.junit.Test
public void TestOne() throws Exception {
    RowReader rowReader = RowReader.create(User.class);
    POIExcelUtils poiExcelUtils = new POIExcelUtils("D://POI-test/test.xlsx", 3);
    poiExcelUtils.setRowReader(rowReader);
    poiExcelUtils.process();
    List contents = (List)rowReader.getContents();
    System.out.println(contents);
}

执行日志:[User(roleIds=null, name=6, password=7), User(roleIds=null, name=8, password=9)]


测试文件
5:SXSSF(Streaming Usermodel API)

SXSSF是XSSF的API兼容流式扩展,用于在必须生成非常大的电子表格且堆空间有限。
SXSSF通过限制缓存的行数来实现低内存占用写操作。
示例:

/**
 * 

* @param datas 需要写入得数据 * @param filePath 文件链接 * @param minCellnum 列数 * @param maxRowNum 最大行数 * @return * @throws IOException */ public static Boolean write(List datas, String filePath, Integer minCellnum, Integer maxRowNum) throws Exception { // 创建文件 SXSSFWorkbook wb = new SXSSFWorkbook(-1); // 创建工作表 Sheet sh = wb.createSheet(); // 数据量 int size = datas.size(); // 获取需要输出的字段集合 Class clazz = datas.get(0).getClass(); // 获取实体属性集合 Field[] fields = clazz.getDeclaredFields(); List writeFileId = new ArrayList<>(); // 排除被 final 或 transient 修饰的字段 for (Field field : fields) { String modifier = Modifier.toString(field.getModifiers()); if (!(modifier.indexOf("final") > -1 || modifier.indexOf("transient") > -1)) { writeFileId.add(field); } } // ========================开始设置第一行 Row row0 = sh.createRow(0); for(int cellnum = 0; cellnum < writeFileId.size(); cellnum++){ Field field = writeFileId.get(cellnum); Cell cell = row0.createCell(cellnum); String cellValue = field.getName(); cell.setCellValue(cellValue); } // ==========================结束设置第一行 // 循环数据创建行 for(int rownum = 0; rownum < size; rownum++){ // 第一行被占据了 Row row = sh.createRow(rownum + 1); for(int cellnum = 0; cellnum < writeFileId.size(); cellnum++){ Field field = writeFileId.get(cellnum); // 设置属性可以直接的进行访问 field.setAccessible(true); Object obj; // 数据实体对象 String cellValue; // 单元格的值 Cell cell = row.createCell(cellnum); // 属性是基础数据类型 if (field.getType().isPrimitive()) { cellValue = field.get(datas.get(rownum)).toString(); }else { obj = field.getType().newInstance(); // 属性是日期类型 if (obj instanceof Date) { Date date = (Date)field.get(datas.get(rownum)); cellValue = DateUtils.format(date, DateUtils.FORMAT_SEC_EN); } else { cellValue = field.get(datas.get(rownum)).toString(); } } cell.setCellValue(cellValue); } if(rownum % maxRowNum == 0) { ((SXSSFSheet)sh).flushRows(maxRowNum); } } FileOutputStream out = new FileOutputStream(filePath); wb.write(out); out.close(); wb.dispose(); wb.close(); return true; }

测试: 从测试文件test.xlsx读取出来的数据写到文件test2.xlsx

@org.junit.Test
public void TestOne() throws Exception {
    RowReader rowReader = RowReader.create(User.class);
    POIExcelUtils poiExcelUtils = new POIExcelUtils("D://POI-test/test.xlsx", 3);
    poiExcelUtils.setRowReader(rowReader);
    poiExcelUtils.process();
    List contents = (List)rowReader.getContents();
    Boolean write = POIWriteUtils.write(contents, "D://POI-test/test2.xlsx", 2, 100);
    if (write) {
        log.info(write.toString());
    }
}

你可能感兴趣的:(SpringBoot + Vue 后台管理系统(七):POI读写操作)