All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nbcp.myoql.db.excel.ExcelComponent.kt Maven / Gradle / Ivy

The newest version!
package nbcp.myoql.db.excel


import nbcp.base.comm.JsonMap
import nbcp.base.extend.*
import nbcp.base.extend.*
import nbcp.myoql.model.DataTable
import org.apache.poi.ooxml.util.SAXHelper
import org.apache.poi.openxml4j.opc.OPCPackage
import org.apache.poi.poifs.filesystem.FileMagic
import org.apache.poi.ss.usermodel.*
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable
import org.apache.poi.xssf.eventusermodel.XSSFReader
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler
import org.apache.poi.xssf.streaming.SXSSFWorkbook
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.xml.sax.InputSource
import java.io.InputStream
import java.io.OutputStream
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.*


fun Cell?.getStringValue(evaluator: FormulaEvaluator): String {
    if (this == null) return ""
    return when (this.cellType) {
        CellType.BLANK -> ""
        CellType.BOOLEAN -> this.booleanCellValue.AsString()
        CellType.NUMERIC -> {
            var longValue = this.numericCellValue.AsLong()
            if (this.numericCellValue - longValue == 0.0) {
                return longValue.toString()
            }

            this.numericCellValue.AsString()
        }

        CellType.STRING -> this.stringCellValue
        CellType.FORMULA -> evaluator.evaluate(this).formatAsString() //应该求值
        else -> ""
    }
}

/**
 * Excel 导入导出。
 * 如果是小文件,可以是内存流;如果是大文件,最好是物理磁盘文件流
 */
class ExcelComponent(val excelStream: () -> InputStream) {
    val sheetNames: Array
        get() {
            var ret = mutableListOf()
            var stream = excelStream()

//            if (stream.markSupported()) {
//                stream.reset();
//            }

            WorkbookFactory.create(stream).use { book ->
                for (i in 0..(book.numberOfSheets - 1)) {
                    ret.add(book.getSheetAt(i).sheetName)
                }
            }
            return ret.toTypedArray();
        }


    fun select(sheetName: String = ""): ExcelSheetComponent {
        return ExcelSheetComponent(sheetName.AsString(sheetNames.first()), excelStream);
    }

    fun select(sheetIndex: Int): ExcelSheetComponent {
        return ExcelSheetComponent(sheetNames.elementAt(sheetIndex), excelStream);
    }

    class ExcelSheetComponent(val sheetName: String, val excelStream: () -> InputStream) {
        private var columns: Array = arrayOf()
        private var rowOffset: Int = 0;
        private var pks: Array = arrayOf()
        private var strictMode: Boolean = true;


        private fun getStream(): InputStream {
            var stream = excelStream();
//            if (stream.markSupported()) {
//                stream.reset();
//            }
            return stream;
        }

        fun setStrictMode(value: Boolean): ExcelSheetComponent {
            this.strictMode = value;
            return this;
        }

        fun setColumns(vararg columns: String): ExcelSheetComponent {
            this.columns = columns;
            return this;
        }

        fun setRowOffset(rowOffset: Int): ExcelSheetComponent {
            this.rowOffset = rowOffset;
            return this;
        }

        fun setPks(vararg pks: String): ExcelSheetComponent {
            this.pks = pks;
            return this;
        }

        private fun getHeaderColumnsIndexMap(
            headerRow: Row,
            columns: Array,
            evaluator: FormulaEvaluator
        ): LinkedHashMap {

            var columnDataIndexs = linkedMapOf()
            for (columnIndex in headerRow.firstCellNum.AsInt()..headerRow.lastCellNum) {
                var columnName = headerRow.getCell(columnIndex).getStringValue(evaluator).trim()
                if (columnName.isEmpty()) {
                    continue
                }

                if (columns.contains(columnName) == false) {
                    continue
                }
                columnDataIndexs.set(columnIndex, columnName)
            }

            return columnDataIndexs;
        }

        /**
         * 从Sheet中读出列
         */
        fun readSheetColumns(): List {
            val fm = FileMagic.prepareToCheckMagic(getStream()).use { file ->
                return@use FileMagic.valueOf(file)
            }

            var list = listOf()

            var filter2: (Map) -> Boolean = f2@{ oriData ->
                list = oriData.values.toList();
                return@f2 false
            }


            /*
POI提供了HSSF、XSSF以及SXSSF三种方式操作Excel。
HSSF:Excel97-2003版本,扩展名为.xls。一个sheet最大行数65536,最大列数256。
XSSF:Excel2007版本开始,扩展名为.xlsx。一个sheet最大行数1048576,最大列数16384。
SXSSF:是在XSSF基础上,POI3.8版本开始提供的支持低内存占用的操作方式,扩展名为.xlsx。
 */
            when (fm) {
                FileMagic.OOXML -> readOpenXmlExcelData({ _, _ -> false }, filter2);
                FileMagic.OLE2 -> readOle2ExcelData({ _, _ -> false }, filter2)
                else -> throw RuntimeException("不识别的类型:${fm}")
            }

            return list;
        }

        /**
         * 读取数据
         */
        @JvmOverloads
        fun  getDataTable(
            type: Class,
            filter: ((JsonMap, Map) -> Boolean)? = null
        ): DataTable {
            var dt = DataTable(type)

            var pk_values = mutableListOf()

            readData { row, oriData ->
                //判断该行是否是主键空值.
                //主键全空.
                if (pks.any()) {
                    var pk_map = row.filterKeys { pks.contains(it) }
                    var pk_value = pks.map { pk_map.get(it) }.joinToString(",")

                    if (pk_values.contains(pk_value)) {
                        throw RuntimeException("发现第 ${pk_values.size + 1} 行存在重复数据!")
                    }
                    pk_values.add(pk_value);
                }

                if (filter != null) {
                    if (filter(row, oriData) == false) {
                        return@readData false;
                    }
                }

                dt.rows.add(row.ConvertJson(type));

                return@readData true;
            }

            pk_values.clear();
            return dt;
        }


        /**读取数据,跳过 offset_row 。
         * @param filter
         */
        fun readData(filter: (JsonMap, Map) -> Boolean) {
            if (columns.isEmpty()) {
                return;
            }

            if (pks.isEmpty() == false) {
                var ext_pks = pks.toList().minus(columns);
                if (ext_pks.any()) {
                    throw RuntimeException("${sheetName}多余的主键定义:${ext_pks.joinToString(",")}");
                }
            }

            val fm = FileMagic.prepareToCheckMagic(getStream()).use { file ->
                return@use FileMagic.valueOf(file)
            }

            var lined = 0;
            var filter2: (JsonMap, Map) -> Boolean = f2@{ row, oriData ->
                lined++;
                if (row.any() == false) {
                    return@f2 false;
                }

                if (pks.any()) {
                    var pk_map = row.filterKeys { pks.contains(it) }

                    if (pk_map.any() == false) {
                        throw RuntimeException("找不到主键的值!,行:${lined}}")
                    }

                    var pk_empty_map = pk_map.filter { it.value.AsString().isEmpty() }
                    if (pk_empty_map.any()) {
                        throw RuntimeException(
                            "发现主键空值,行:${lined}, 列: ${
                                pk_empty_map.map { it.key }.joinToString(",")
                            }"
                        )
                    }
                }

                return@f2 filter(row, oriData);
            }


            /*
POI提供了HSSF、XSSF以及SXSSF三种方式操作Excel。
HSSF:Excel97-2003版本,扩展名为.xls。一个sheet最大行数65536,最大列数256。
XSSF:Excel2007版本开始,扩展名为.xlsx。一个sheet最大行数1048576,最大列数16384。
SXSSF:是在XSSF基础上,POI3.8版本开始提供的支持低内存占用的操作方式,扩展名为.xlsx。
 */
            when (fm) {
                FileMagic.OOXML -> readOpenXmlExcelData(filter2, { true });
                FileMagic.OLE2 -> readOle2ExcelData(filter2, { true })
                else -> throw RuntimeException("不识别的类型:${fm}")
            }

        }


        /**回写数据 按 data.tableName == sheetName
         * @param getRowData: 返回 null 停止。
         */
        fun writeData(outputStream: OutputStream, getRowData: (Int) -> Map?) {
            OPCPackage.open(getStream()).use { xlsxPackage ->
                var book = SXSSFWorkbook(XSSFWorkbook(xlsxPackage), 1000)

                //公式执行器
                var evaluator = book.creationHelper.createFormulaEvaluator()

                //生成一个sheet1
                val sheet = book.xssfWorkbook.getSheet(sheetName);
                var header_row = sheet.getRow(rowOffset)
                // key: excel 中的 列的索引 , value = column_name
                var columns_index_map = getHeaderColumnsIndexMap(header_row, columns, evaluator);


                var dataRowIndex = -1;
                while (true) {
                    dataRowIndex++;

                    var dbRowData = getRowData(dataRowIndex)
                    if (dbRowData == null) {
                        break;
                    }
                    if (dbRowData.any() == false) {
                        break;
                    }

                    var excelRowIndex = dataRowIndex + 1 + rowOffset
                    var excelRow = sheet.getRow(excelRowIndex)
                    if (excelRow == null) {
                        excelRow = sheet.createRow(excelRowIndex)
                    }


                    for (columnIndex in columns_index_map.keys) {
                        var columnName = columns_index_map.get(columnIndex)

                        var dbValue = dbRowData.get(columnName)
                        if (dbValue == null) {
                            continue;
                        }

                        var cell = excelRow.getCell(columnIndex)
                        if (cell == null) {
                            cell = excelRow.createCell(columnIndex)
                        }

                        if (dbValue is String) {
                            cell.setCellValue(dbValue.AsString())
                        } else if (dbValue is Number) {
                            cell.setCellValue(dbValue.AsDouble())
                        } else if (dbValue is Boolean) {
                            cell.setCellValue(dbValue.AsBoolean())
                        } else if (dbValue is LocalDateTime ||
                            dbValue is LocalDate ||
                            dbValue is Date
                        ) {
                            cell.setCellValue(dbValue.AsDate())
                        } else {
                            cell.setCellValue(dbValue.AsString())
                        }
                    }
                }

                sheet.workbook.write(outputStream)
            }
        }


        fun  writeData(outputStream: OutputStream, table: DataTable) {
            writeData(outputStream) { rowIndex ->
                return@writeData table.rows.getOrNull(rowIndex)?.ConvertType(JsonMap::class.java) as JsonMap?
            }
        }


        private fun readOle2ExcelData(
            filter: (JsonMap, Map) -> Boolean,
            sheetColumnsCallback: (Map) -> Boolean
        ) {
            WorkbookFactory.create(getStream()).use { book ->

                var sheet: Sheet;

                try {
                    if (book.numberOfSheets == 1) {
                        sheet = book.getSheetAt(0)
                    } else {
                        sheet = book.getSheet(sheetName);
                    }
                } catch (e: java.lang.Exception) {
                    throw java.lang.Exception("打不开Excel文件的Sheet ${sheetName} !")
                }


                //公式执行器
                var evaluator = book.creationHelper.createFormulaEvaluator()
                var header_row = sheet.getRow(rowOffset)
                // key: excel 中的 列的索引 , value = column_name
                var columns_index_map = getHeaderColumnsIndexMap(header_row, columns, evaluator);

                if (sheetColumnsCallback(columns_index_map) == false) {
                    return;
                }

                for (rowIndex in (rowOffset + 1)..sheet.lastRowNum) {
                    var row = sheet.getRow(rowIndex)
                    if (row == null) {
                        break
                    }
                    var oriData = mutableMapOf()

                    for (columnIndex in (row.firstCellNum - 1)..(row.lastCellNum - 1)) {
                        var cell = row.getCell(columnIndex);
                        if (cell == null) {
                            continue
                        }

                        //处理 日期,时间 格式。
                        if (cell.cellType == CellType.NUMERIC) {
                            var value = cell.numericCellValue.toBigDecimal().toPlainString()
                            if (value.indexOf(".") < 0 || "General" == cell.cellStyle.dataFormatString) {
                                oriData.set(columnIndex, value);
                                continue;
                            }

                            if (cell.cellStyle.dataFormatString.indexOf("yy") >= 0 &&
                                cell.cellStyle.dataFormatString.indexOf("m") >= 0 &&
                                cell.cellStyle.dataFormatString.indexOf("d") >= 0 &&
                                cell.cellStyle.dataFormatString.indexOf("h:mm:ss") >= 0
                            ) {

                                oriData.set(columnIndex, cell.dateCellValue.AsLocalDateTime().AsString());
                                continue;
                            } else if (cell.cellStyle.dataFormatString.indexOf("h:mm:ss") >= 0) {
                                oriData.set(columnIndex, cell.dateCellValue.AsLocalTime().AsString());
                                continue;
                            } else if (cell.cellStyle.dataFormatString.indexOf("yy") >= 0 &&
                                cell.cellStyle.dataFormatString.indexOf("m") >= 0 &&
                                cell.cellStyle.dataFormatString.indexOf("d") >= 0
                            ) {
                                oriData.set(columnIndex, cell.dateCellValue.AsLocalDate().AsString());
                                continue;
                            }
                        }

                        oriData.set(columnIndex, cell.getStringValue(evaluator).AsString().trim())
                    }


//                if (columns_index_map.size != columns.size) {
//                    var ext_columns = columns.minus(columns_index_map.values);
//                    throw RuntimeException("找不到列:${ext_columns.joinToString(",")}")
//                }

                    var rowData = JsonMap();
                    for (columnIndex in columns_index_map.keys) {
                        var columnName = columns_index_map.get(columnIndex)!!;
                        rowData.set(columnName, oriData[columnIndex])
                    }


                    if (filter(rowData, oriData) == false) {
                        return;
                    }
                }
            }
        }


        private fun readOpenXmlExcelData(
            filter: (JsonMap, Map) -> Boolean,
            sheetColumnsCallback: (Map) -> Boolean
        ) {
            OPCPackage.open(getStream()).use { xlsxPackage ->
                var xssfReader = XSSFReader(xlsxPackage)

                var iter = xssfReader.sheetsData as XSSFReader.SheetIterator
                while (iter.hasNext()) {
                    iter.next().use { stream ->
                        if (iter.sheetName == sheetName) {
                            getSheetData(xlsxPackage, xssfReader, stream, filter, sheetColumnsCallback)
                            return;
                        }
                    }
                }

                throw java.lang.Exception("找不到Excel文件的Sheet ${sheetName} !")
            }
        }


        private fun getSheetData(
            xlsxPackage: OPCPackage,
            xssfReader: XSSFReader,
            sheetInputStream: InputStream,
            filter: ((JsonMap, Map) -> Boolean),
            sheetColumnsCallback: ((Map) -> Boolean)
        ) {

            var strings = ReadOnlySharedStringsTable(xlsxPackage);
            var styles = xssfReader.getStylesTable();
            var formatter = DataFormatter()
            var sheetSource = InputSource(sheetInputStream);
            var sheetParser = SAXHelper.newXMLReader();

            try {
                sheetParser.contentHandler = XSSFSheetXMLHandler(
                    styles,
                    null,
                    strings,
                    SheetContentReader(
                        sheetParser,
                        columns,
                        filter,
                        sheetColumnsCallback,
                        this.rowOffset,
                        this.strictMode
                    ),
                    formatter,
                    false
                );
                sheetParser.parse(sheetSource)
            } catch (e: Exception) {
                if (e.cause is ReturnException == false) {
                    throw e;
                }
            }
            return;
        }

    }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy