• .NET NPOI导出Excel详解


    NPOI,顾名思义,就是POI的.NET版本。那POI又是什么呢?POI是一套用Java写成的库,能够帮助开发者在没有安装微软Office的情况下读写Office的文件。

    支持的文件格式包括xls, doc, ppt等。

    官方网站:http://npoi.codeplex.com/

    nuget直接获取使用

    一、NPOI生成Excel

    复制代码
                //创建工作薄
                var workbook = new HSSFWorkbook();
                //创建表
                var table = workbook.CreateSheet("joye.net");
    
                //模拟20行20列数据
                for (var i = 0; i < 20; i++)
                {
                    var row = table.CreateRow(i);
                    for (int j = 0; j < 20; j++)
                    {
                        var cell = row.CreateCell(j);
                        cell.SetCellValue(j);
                    }
                }
                //打开xls文件,如没有则创建,如存在则在创建是不要打开该文件
                using (var fs = File.OpenWrite(@"c:/joye.net.xls"))
                {
                    workbook.Write(fs);   //向打开的这个xls文件中写入mySheet表并保存。
                    Console.WriteLine("生成成功");
                }
    复制代码

    二、NPOI读取Excel

    复制代码
    using (var fs = File.OpenRead(@"c:/joye.net.xls")) 
                {
                    //把xls文件中的数据写入workbook1中
                    var workbook1 = new HSSFWorkbook(fs);
                    for (var i = 0; i < workbook1.NumberOfSheets; i++)
                    {
                        var sheet = workbook1.GetSheetAt(i);
                        for (var j = 0; j <= sheet.LastRowNum; j++)
                        {   
                            //读取当前行数据
                            var row = sheet.GetRow(j);
                            if (row != null)
                            {
                                for (var k = 0; k <= row.LastCellNum; k++)
                                {   //当前表格
                                    var cell = row.GetCell(k);
                                    if (cell != null)
                                    {
                                        Console.Write(cell.ToString() + "  ");
                                    }
                                }
                            }
                            Console.WriteLine();
                        }
                    }
                }
    复制代码

    读出的结果

     三、简单学习

     学习代码
     代码调用

    四、NPOI导出Excel 65536问题

    复制代码
            public static HSSFWorkbook BuildWorkbook(DataTable dt)
            {
                var book = new HSSFWorkbook();  
    
                ISheet sheet = book.CreateSheet("Sheet1");
                //Data Rows
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    IRow drow = sheet.CreateRow(i);
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        ICell cell = drow.CreateCell(j, CellType.String);
                        cell.SetCellValue(dt.Rows[i][j].ToString());
                    }
                }
                //自动列宽
                for (int i = 0; i <= dt.Columns.Count; i++)
                    sheet.AutoSizeColumn(i, true);
                return book;
            }
    复制代码

    NPOI导出Excel超过65536会报异常,原来是由于NPOI这个动态库导致的,然后看了下版本,发现是1.2.5。然后百度了下,发现这个版本的NPOI只支持office2003,二office2003最多支持65536行

    解决方式:

    1、只是在插入数据的时候,加个判断,如果数据条数大于65536时,就在创建一个sheet

    复制代码
            //65536判断处理
            public static HSSFWorkbook BuildWorkbook(DataTable dt)
            {
                var book = new HSSFWorkbook();
    
                ISheet sheet1 = book.CreateSheet("Sheet1");
                ISheet sheet2 = book.CreateSheet("Sheet2");
    
                //填充数据
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    if (i < 65536)
                    {
                        IRow drow = sheet1.CreateRow(i);
                        for (int j = 0; j < dt.Columns.Count; j++)
                        {
                            ICell cell = drow.CreateCell(j, CellType.String);
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                        }
                    }
                    if (i >= 65536)
                    {
                        IRow drow = sheet2.CreateRow(i - 65536);
                        for (int j = 0; j < dt.Columns.Count; j++)
                        {
                            ICell cell = drow.CreateCell(j, CellType.String);
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                        }
                    }
    
                }
    
                //自动列宽
                for (int i = 0; i <= dt.Columns.Count; i++)
                {
                    sheet1.AutoSizeColumn(i, true);
                    sheet2.AutoSizeColumn(i, true);
                }
                return book;
            }
    复制代码

    2、考虑使用高版本Office,使用用对象支持高版本的NPOI

    复制代码
            //高版本
            public static XSSFWorkbook BuildWorkbook(DataTable dt)
            {
                var book = new XSSFWorkbook();
                ISheet sheet = book.CreateSheet("Sheet1");
                //Data Rows
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    IRow drow = sheet.CreateRow(i);
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        ICell cell = drow.CreateCell(j, CellType.String);
                        cell.SetCellValue(dt.Rows[i][j].ToString());
                    }
                }
                //自动列宽
                for (int i = 0; i <= dt.Columns.Count; i++)
                    sheet.AutoSizeColumn(i, true);
    
                return book;
            }
    复制代码

    五、web开发中导出Excel

    复制代码
            public static void ExportExcel(DataTable dt, string fileName = "")
            {
                //生成Excel
                IWorkbook book = BuildWorkbook(dt);
    
                //web 下载
                if (fileName == "")
                    fileName = string.Format("{0:yyyyMMddHHmmssffff}", DateTime.Now);
                fileName = fileName.Trim();
                string ext = Path.GetExtension(fileName);
    
                if (ext.ToLower() == ".xls" || ext.ToLower() == ".xlsx")
                    fileName = fileName.Replace(ext, string.Empty);
    
                HttpResponse httpResponse = HttpContext.Current.Response;
                httpResponse.Clear();
                httpResponse.Buffer = true;
                httpResponse.Charset = Encoding.UTF8.BodyName;
                httpResponse.AppendHeader("Content-Disposition", "attachment;filename=" + fileName + ".xls");
                httpResponse.ContentEncoding = Encoding.UTF8;
                httpResponse.ContentType = "application/vnd.ms-excel; charset=UTF-8";
                book.Write(httpResponse.OutputStream);
                httpResponse.End();
            }
    复制代码
    复制代码
    using NPOI.HSSF.UserModel;
    using NPOI.SS.UserModel;
    using NPOI.XSSF.UserModel;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Reflection;
    using System.Text;
    using System.Linq;
    
    namespace Gto.Report.Contract
    {
        public static class ExcelHelperForCs
        {
    
            /// <summary>
            ///  组装workbook.
            /// </summary>
            /// <param name="dt">dataTable资源</param>
            /// <param name="columnHeader">表头</param>
            /// <returns></returns>
            public static HSSFWorkbook BuildWorkbook1(DataTable dt, string columnHeader = "")
            {
                var workbook = new HSSFWorkbook();
                ISheet sheet = workbook.CreateSheet(string.IsNullOrWhiteSpace(dt.TableName) ? "Sheet1" : dt.TableName);
    
                var dateStyle = workbook.CreateCellStyle();
                var format = workbook.CreateDataFormat();
                dateStyle.DataFormat = format.GetFormat("yyyy-mm-dd");
    
                //取得列宽
                var arrColWidth = new int[dt.Columns.Count];
                foreach (DataColumn item in dt.Columns)
                {
                    arrColWidth[item.Ordinal] = Encoding.GetEncoding(936).GetBytes(item.ColumnName.ToString()).Length;
                }
                for (var i = 0; i < dt.Rows.Count; i++)
                {
                    for (var j = 0; j < dt.Columns.Count; j++)
                    {
                        int intTemp = Encoding.GetEncoding(936).GetBytes(dt.Rows[i][j].ToString()).Length;
                        if (intTemp > arrColWidth[j])
                        {
                            arrColWidth[j] = intTemp;
                        }
                    }
                }
                int rowIndex = 0;
                foreach (DataRow row in dt.Rows)
                {
                    #region 表头 列头
                    if (rowIndex == 65535 || rowIndex == 0)
                    {
                        if (rowIndex != 0)
                        {
                            sheet = workbook.CreateSheet();
                        }
    
                        #region 表头及样式
                        {
                            var headerRow = sheet.CreateRow(0);
                            headerRow.HeightInPoints = 25;
                            headerRow.CreateCell(0).SetCellValue(columnHeader);
                            //CellStyle
                            ICellStyle headStyle = workbook.CreateCellStyle();
                            headStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;// 左右居中    
                            headStyle.VerticalAlignment = VerticalAlignment.Center;// 上下居中 
                            // 设置单元格的背景颜色(单元格的样式会覆盖列或行的样式)    
                            headStyle.FillForegroundColor = (short)11;
                            //定义font
                            IFont font = workbook.CreateFont();
                            font.FontHeightInPoints = 20;
                            font.Boldweight = 700;
                            headStyle.SetFont(font);
                            headerRow.GetCell(0).CellStyle = headStyle;
                            sheet.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 0, 0, dt.Columns.Count - 1));
                        }
                        #endregion
    
    
                        #region 列头及样式
                        {
                            var headerRow = sheet.CreateRow(1);
                            //CellStyle
                            ICellStyle headStyle = workbook.CreateCellStyle();
                            headStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;// 左右居中    
                            headStyle.VerticalAlignment = VerticalAlignment.Center;// 上下居中 
                            //定义font
                            IFont font = workbook.CreateFont();
                            font.FontHeightInPoints = 10;
                            font.Boldweight = 700;
                            headStyle.SetFont(font);
    
                            foreach (DataColumn column in dt.Columns)
                            {
                                headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
                                headerRow.GetCell(column.Ordinal).CellStyle = headStyle;
                                sheet.SetColumnWidth(column.Ordinal, (arrColWidth[column.Ordinal] + 1) * 256);
                            }
                        }
                        #endregion
                        if (columnHeader != "")
                        {
                            //header row
                            IRow row0 = sheet.CreateRow(0);
                            for (int i = 0; i < dt.Columns.Count; i++)
                            {
                                ICell cell = row0.CreateCell(i, CellType.String);
                                cell.SetCellValue(dt.Columns[i].ColumnName);
                            }
                        }
    
                        rowIndex = 2;
                    }
                    #endregion
    
    
                    #region 内容
                    var dataRow = sheet.CreateRow(rowIndex);
                    foreach (DataColumn column in dt.Columns)
                    {
                        var newCell = dataRow.CreateCell(column.Ordinal);
    
                        string drValue = row[column].ToString();
    
                        switch (column.DataType.ToString())
                        {
                            case "System.String"://字符串类型
                                newCell.SetCellValue(drValue);
                                break;
                            case "System.DateTime"://日期类型
                                DateTime dateV;
                                DateTime.TryParse(drValue, out dateV);
                                newCell.SetCellValue(dateV);
    
                                newCell.CellStyle = dateStyle;//格式化显示
                                break;
                            case "System.Boolean"://布尔型
                                bool boolV = false;
                                bool.TryParse(drValue, out boolV);
                                newCell.SetCellValue(boolV);
                                break;
                            case "System.Int16"://整型
                            case "System.Int32":
                            case "System.Int64":
                            case "System.Byte":
                                int intV = 0;
                                int.TryParse(drValue, out intV);
                                newCell.SetCellValue(intV);
                                break;
                            case "System.Decimal"://浮点型
                            case "System.Double":
                                double doubV = 0;
                                double.TryParse(drValue, out doubV);
                                newCell.SetCellValue(doubV);
                                break;
                            case "System.DBNull"://空值处理
                                newCell.SetCellValue("");
                                break;
                            default:
                                newCell.SetCellValue("");
                                break;
                        }
    
                    }
                    #endregion
    
                    rowIndex++;
                }
                //自动列宽
                for (int i = 0; i <= dt.Columns.Count; i++)
                    sheet.AutoSizeColumn(i, true);
    
                return workbook;
            }
            public static DataTable ToDataTable<T>(IList<T> items)
            {
                var tb = new DataTable(typeof(T).Name);
    
                PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    
                foreach (PropertyInfo prop in props)
                {
                    Type t = GetCoreType(prop.PropertyType);
                    tb.Columns.Add(prop.Name, t);
                }
    
                foreach (T item in items)
                {
                    var values = new object[props.Length];
    
                    for (int i = 0; i < props.Length; i++)
                    {
                        values[i] = props[i].GetValue(item, null);
                    }
    
                    tb.Rows.Add(values);
                }
    
                return tb;
            }
            public static bool IsNullable(Type t)
            {
                return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
            }
    
            public static Type GetCoreType(Type t)
            {
                if (t != null && IsNullable(t))
                {
                    if (!t.IsValueType)
                    {
                        return t;
                    }
                    else
                    {
                        return Nullable.GetUnderlyingType(t);
                    }
                }
                else
                {
                    return t;
                }
            }
    
            /// <summary>
            /// DataTable导出Excel2007(.xlsx)
            /// </summary>
            /// <param name="dt">DataTable</param>
            /// <param name="file">文件路径(.xlsx)</param>
            /// <param name="sheetname">Excel工作表名</param>
            public static void TableToExcelForXLSX2007(DataTable dt, string file, string sheetname)
            {
                XSSFWorkbook xssfworkbook = new XSSFWorkbook();//建立Excel2007对象
                ISheet sheet = xssfworkbook.CreateSheet(sheetname);//新建一个名称为sheetname的工作簿
    
                //设置基本样式
                ICellStyle style = xssfworkbook.CreateCellStyle();
                style.WrapText = true;
                IFont font = xssfworkbook.CreateFont();
                font.FontHeightInPoints = 9;
                font.FontName = "Arial";
                style.SetFont(font);
    
                //设置统计样式
                ICellStyle style1 = xssfworkbook.CreateCellStyle();
                style1.WrapText = true;
                IFont font1 = xssfworkbook.CreateFont();
                font1.FontHeightInPoints = 9;
                font1.Boldweight = (short)NPOI.SS.UserModel.FontBoldWeight.Bold;
                font1.FontName = "Arial";
                style1.SetFont(font1);
    
                //设置大类样式
                ICellStyle style2 = xssfworkbook.CreateCellStyle();
                style2.WrapText = true;
                //style2.FillBackgroundColor = NPOI.HSSF.Util.HSSFColor.Gold.Index;
                //style2.FillPattern = FillPattern.SolidForeground;
                IFont font2 = xssfworkbook.CreateFont();
                font2.FontHeightInPoints = 9;
                font2.Boldweight = (short)NPOI.SS.UserModel.FontBoldWeight.Bold;
                font2.FontName = "Arial";
                style2.SetFont(font2);
    
    
                //设置列名
                IRow row = sheet.CreateRow(0);
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    ICell cell = row.CreateCell(i);
                    var rowName = dt.Columns[i].ColumnName;
                    string rowRealName = "";
                    switch (rowName)
                    {
                        case "IncomeType":
                            rowRealName = "交易类型";
                            break;
                        case "CreateDate":
                            rowRealName = "发生日期";
                            break;
                        case "ChangeAmount":
                            rowRealName = "合计金额";
                            break;
                        case "SubsectionName":
                            rowRealName = "分段名称";
                            break;
                        case "CorporateName":
                            rowRealName = "公司名称";
                            break;
                        case "Province":
                            rowRealName = "省份";
                            break;
                        case "ShuntName":
                            rowRealName = "项目";
                            break;
                        case "CountAmount":
                            rowRealName = "本年累计金额";
                            break;
                        default:
                            rowRealName = "";
                            break;
                    }
                    cell.SetCellValue(rowRealName);
                    cell.CellStyle = style;
                }
                int paymentRowIndex = 1;
                //单元格赋值
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    IRow row1 = sheet.CreateRow(i + 1);
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        ICell cell = row1.CreateCell(j);
    
                        if (dt.Rows[i][j].ToString().Contains("小计") || dt.Rows[i][j].ToString().Contains("流量净额"))
                        {
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                            cell.CellStyle = style2;
                        }
                        else if (dt.Rows[i][j].ToString().Contains("一") || dt.Rows[i][j].ToString().Contains("二") || dt.Rows[i][j].ToString().Contains("三"))
                        {
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                            cell.CellStyle = style1;
                        }
                        else
                        {
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                            cell.CellStyle = style;
                        }
    
                    }
                    paymentRowIndex++;
                }
    
                //列宽自适应,只对英文和数字有效
                for (int i = 0; i <= dt.Rows.Count; i++)
                {
                    sheet.AutoSizeColumn(i);
                }
                //获取当前列的宽度,然后对比本列的长度,取最大值
                for (int columnNum = 0; columnNum <= dt.Rows.Count; columnNum++)
                {
                    int columnWidth = sheet.GetColumnWidth(columnNum) / 256;
                    for (int rowNum = 1; rowNum <= sheet.LastRowNum; rowNum++)
                    {
                        IRow currentRow;
                        //当前行未被使用过
                        if (sheet.GetRow(rowNum) == null)
                        {
                            currentRow = sheet.CreateRow(rowNum);
                        }
                        else
                        {
                            currentRow = sheet.GetRow(rowNum);
                        }
    
                        if (currentRow.GetCell(columnNum) != null)
                        {
                            ICell currentCell = currentRow.GetCell(columnNum);
                            int length = Encoding.Default.GetBytes(currentCell.ToString()).Length;
                            if (columnWidth < length)
                            {
                                columnWidth = length;
                            }
                        }
                    }
                    sheet.SetColumnWidth(columnNum, columnWidth * 256);
                }
    
                using (System.IO.Stream stream = System.IO.File.OpenWrite(file))
                {
                    //写入文件
                    xssfworkbook.Write(stream);
                    stream.Close();
                }
            }
    
    
            /// <summary>
            /// DataTable导出Excel2003(.xls)
            /// </summary>
            /// <param name="dt">DataTable</param>
            /// <param name="file">文件路径(.xls)</param>
            /// <param name="sheetname">Excel工作表名</param>
            public static void TableToExcelForXLSX2003(DataTable dt, string file, string sheetname)
            {
                HSSFWorkbook xssfworkbook = new HSSFWorkbook();//建立Excel2003对象
                HSSFSheet sheet = (HSSFSheet)xssfworkbook.CreateSheet(sheetname);//新建一个名称为sheetname的工作簿
    
    
                //设置基本样式
                ICellStyle style = xssfworkbook.CreateCellStyle();
                style.WrapText = true;
                IFont font = xssfworkbook.CreateFont();
                font.FontHeightInPoints = 9;
                font.FontName = "Arial";
                style.SetFont(font);
    
                //设置统计样式
                ICellStyle style1 = xssfworkbook.CreateCellStyle();
                style1.WrapText = true;
                IFont font1 = xssfworkbook.CreateFont();
                font1.FontHeightInPoints = 9;
                font1.Boldweight = (short)NPOI.SS.UserModel.FontBoldWeight.Bold;
                font1.FontName = "Arial";
                style1.SetFont(font1);
    
                //设置大类样式
                ICellStyle style2 = xssfworkbook.CreateCellStyle();
                style2.WrapText = true;
                //style2.FillBackgroundColor = NPOI.HSSF.Util.HSSFColor.Gold.Index;
                //style2.FillPattern = FillPattern.SolidForeground;
                IFont font2 = xssfworkbook.CreateFont();
                font2.FontHeightInPoints = 9;
                font2.Boldweight = (short)NPOI.SS.UserModel.FontBoldWeight.Bold;
                font2.FontName = "Arial";
                style2.SetFont(font2);
    
                //设置列名
                HSSFRow row = (HSSFRow)sheet.CreateRow(0);
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    ICell cell = (ICell)row.CreateCell(i);
                    var rowName = dt.Columns[i].ColumnName;
                    //cell.SetCellValue(dt.Columns[i].ColumnName);
                    string rowRealName = "";
                    switch (rowName)
                    {
                        case "IncomeType":
                            rowRealName = "交易类型";
                            break;
                        case "CreateDate":
                            rowRealName = "发生日期";
                            break;
                        case "ChangeAmount":
                            rowRealName = "合计金额";
                            break;
                        case "SubsectionName":
                            rowRealName = "分段名称";
                            break;
                        case "CorporateName":
                            rowRealName = "公司名称";
                            break;
                        case "Province":
                            rowRealName = "省份";
                            break;
                        case "ShuntName":
                            rowRealName = "项目";
                            break;
                        case "CountAmount":
                            rowRealName = "本年累计金额";
                            break;
                        default:
                            rowRealName = "";
                            break;
                    }
                    cell.SetCellValue(rowRealName);
                    cell.CellStyle = style;
                }
                int paymentRowIndex = 1;
                //单元格赋值
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    IRow row1 = sheet.CreateRow(i + 1);
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        ICell cell = row1.CreateCell(j);
    
                        if (dt.Rows[i][j].ToString().Contains("小计") || dt.Rows[i][j].ToString().Contains("流量净额"))
                        {
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                            cell.CellStyle = style2;
                        }
                        else if (dt.Rows[i][j].ToString().Contains("一") || dt.Rows[i][j].ToString().Contains("二") || dt.Rows[i][j].ToString().Contains("三"))
                        {
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                            cell.CellStyle = style1;
                        }
                        else
                        {
                            cell.SetCellValue(dt.Rows[i][j].ToString());
                            cell.CellStyle = style;
    
                        }
    
                    }
                    paymentRowIndex++;
                }
                //列宽自适应,只对英文和数字有效
                for (int i = 0; i <= dt.Rows.Count; i++)
                {
                    sheet.AutoSizeColumn(i);
                }
                //获取当前列的宽度,然后对比本列的长度,取最大值
                for (int columnNum = 0; columnNum <= dt.Rows.Count; columnNum++)
                {
                    int columnWidth = sheet.GetColumnWidth(columnNum) / 256;
                    for (int rowNum = 1; rowNum <= sheet.LastRowNum; rowNum++)
                    {
                        IRow currentRow;
                        //当前行未被使用过
                        if (sheet.GetRow(rowNum) == null)
                        {
                            currentRow = sheet.CreateRow(rowNum);
                        }
                        else
                        {
                            currentRow = sheet.GetRow(rowNum);
                        }
    
                        if (currentRow.GetCell(columnNum) != null)
                        {
                            ICell currentCell = currentRow.GetCell(columnNum);
                            int length = Encoding.Default.GetBytes(currentCell.ToString()).Length;
                            if (columnWidth < length)
                            {
                                columnWidth = length;
                            }
                        }
                    }
                    sheet.SetColumnWidth(columnNum, columnWidth * 256);
                }
                using (System.IO.Stream stream = System.IO.File.OpenWrite(file))
                {
                    xssfworkbook.Write(stream);
                    stream.Close();
                }
    
            }
        }
    
    }
    复制代码

    基于.xls模板生成Excel文件有时间再看

    代码下载:https://yunpan.cn/cRBVnTCSchz7k (提取码:779e)

  • 相关阅读:
    javaScript事件(二)事件处理程序
    【前端】模糊图片
    【前端】js转码
    【翻译】Sencha Touch2.4 The Layout System 布局
    Sencha Touch 2.4 callParent() 用法
    2015 WEB前端学习路线图
    Nodejs Express 4.X 中文API 4--- Router篇
    Nodejs Express 4.X 中文API 3--- Response篇
    Nodejs Express 4.X 中文API 2--- Request篇
    Nodejs Express 4.X 中文API 1--- Application篇
  • 原文地址:https://www.cnblogs.com/zhoufangcheng04050227/p/10617070.html
Copyright © 2020-2023  润新知