C#,.net,winform导入Excel功能以及下载Excel文件到本地,并使用SqlBulkCopy把DataTable类型的数据写入到sqlserver数据库中

简介: C#,.net,winform导入Excel功能以及下载Excel文件到本地,并使用SqlBulkCopy把DataTable类型的数据写入到sqlserver数据库中

背景


最近在项目中有一个导入Excel的功能,下文中将介绍如何在C#项目中导入Excel文件,以及实现此功能的宏观思路。


宏观思路


因为项目中的导入功能是用户根据自己的需要去导入Excel文件,那么对于用户上传的格式是有要求的。所以需要让用户先下载我们提供的模板,然后用户可以去编辑模板,再进行上传。


下载模板


下载功能是的数据类型是使用的DataTable,也就是我们在DataTable中为用户提供列的名称放在一个DataTable中,让用户去下载。


具体代码实现


通过我们在代码中设置列的标题(或者让用户可配置),已list集合的方式传入此方法中


/// <summary>
        /// 定义导入数据的模板
        /// 通过for循环给空白的DataTable添加列名称
        /// </summary>
        /// <param name="ColumnNames">模板的列的集合</param>
        /// <returns></returns>
        public DataTable ImportTemplate(List<string> ColumnNames)
        {
            DataTable template = new DataTable();
            DataRow dr = template.NewRow() ;
            template.Rows.Add(dr);
            for(int i = 0; i < ColumnNames.Count; i++)
            {
                template.Columns.Add(ColumnNames[i]);
            }
            return template;
        }


导出文件的具体代码


public void Export(DataTable dtSource,string FileName)
        {
            try {
                SaveFileDialog fileDialog = new SaveFileDialog();
                fileDialog.Filter = "Excel|*.xls|TXT|*.txt|PDF|*.pdf";
                fileDialog.FileName = FileName+"-"+DateTime.Now.ToString("D");
                if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
                {
                    return;
                }
                string FileSavePath = fileDialog.FileName;
                switch (fileDialog.FilterIndex)
                {
                    case 1:
                        ExportXls(FileSavePath, dtSource); //导出xls
                        break;
                    case 2:
                        ExportTxt(FileSavePath, dtSource); //导出txt
                        break;
                    case 3:
                        ExportPDF(FileSavePath, dtSource); //导出pdf
                        break;
                        //case 4:
                        //    ExportDocx(FileSavePath, dtSource); //导出docx
                        //    break;
                }
                MessageBox.Show("文件 "+ FileSavePath + " 导出成功");
            }
            catch(Exception e){
                MessageBox.Show("导出文件失败,请稍后重新尝试"+ e);
            }
        }


有了以上两个方法之后,我们可以在某事件中去触发此这些方法,然后下载文件到用户的本地。


导入文件


上文中已经把导入的下载模板的工作完成,接下来是需要用把编辑好的Excel文件上传到我们程序并同步到数据库中。


上传Excel这里使用的NPOI,在“管理NuGet程序包”中,搜索NPOI找到,下载并安装。


ce52ba8128cff5a1c74446d4902c8222_f4c7e405704a454c805c47d4cb00d446.png


这里可能出现问题是


2f14a6f6928507b6f5b347004ce78d82_a078d692ecae4a109550adf8e60e5f4b.png


解决办法:先安装“SixLabors.Fonts”,需要注意的是:如果直接搜索的话可能找不到,需要把“包括预发行版”勾选上


0764cf17d3edcb09488a3da2460845f4_02b436b9cc0f4065bcb675adc7ffbf50.png


安装好之后,再去下载NPOI。


在项目中添加类ExcelHelper


internal class ExcelHelper
    {
        /// <summary>
        /// 从Excel读取数据,只支持单表
        /// </summary>
        /// <param name="FilePath">文件路径</param>
        public static DataTable ReadFromExcel(string FilePath)
        {
            IWorkbook wk = null;
            string extension = System.IO.Path.GetExtension(FilePath); //获取扩展名
            try
            {
                using (FileStream fs = File.OpenRead(FilePath))
                {
                    if (extension.Equals(".xls")) //2003
                    {
                        wk = new HSSFWorkbook(fs);
                    }
                    else                         //2007以上
                    {
                        wk = new XSSFWorkbook(fs);
                    }
                }
                //读取当前表数据
                ISheet sheet = wk.GetSheetAt(0);
                //构建DataTable
                IRow row = sheet.GetRow(0);
                DataTable result = BuildDataTable(row);
                if (result != null)
                {
                    if (sheet.LastRowNum >= 1)
                    {
                        for (int i = 1; i < sheet.LastRowNum + 1; i++)
                        {
                            IRow temp_row = sheet.GetRow(i);
                            if (temp_row == null) { continue; }//2019-01-14 修复 行为空时会出错
                            List<object> itemArray = new List<object>();
                            for (int j = 0; j < result.Columns.Count; j++)//解决Excel超出DataTable列问题    lqwvje20181027
                            {
                                //itemArray.Add(temp_row.GetCell(j) == null ? string.Empty : temp_row.GetCell(j).ToString());
                                itemArray.Add(GetValueType(temp_row.GetCell(j)));//解决 导入Excel  时间格式问题  lqwvje 20180904
                            }
                            result.Rows.Add(itemArray.ToArray());
                        }
                    }
                }
                return result;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        /// <summary>
        /// 从Excel读取数据,支持多表
        /// </summary>
        /// <param name="FilePath">文件路径</param>
        public static DataSet ReadFromExcels(string FilePath)
        {
            DataSet ds = new DataSet();
            IWorkbook wk = null;
            string extension = System.IO.Path.GetExtension(FilePath); //获取扩展名
            try
            {
                using (FileStream fs = File.OpenRead(FilePath))
                {
                    if (extension.Equals(".xls")) //2003
                    {
                        wk = new HSSFWorkbook(fs);
                    }
                    else                         //2007以上
                    {
                        wk = new XSSFWorkbook(fs);
                    }
                }
                int SheetCount = wk.NumberOfSheets;//获取表的数量
                if (SheetCount < 1)
                {
                    return ds;
                }
                for (int s = 0; s < SheetCount; s++)
                {
                    //读取当前表数据
                    ISheet sheet = wk.GetSheetAt(s);
                    //构建DataTable
                    IRow row = sheet.GetRow(0);
                    if (row == null) { continue; }
                    DataTable tempDT = BuildDataTable(row);
                    tempDT.TableName = wk.GetSheetName(s);
                    if (tempDT != null)
                    {
                        if (sheet.LastRowNum >= 1)
                        {
                            for (int i = 1; i < sheet.LastRowNum + 1; i++)
                            {
                                IRow temp_row = sheet.GetRow(i);
                                if (temp_row == null) { continue; }//2019-01-14 修复 行为空时会出错
                                List<object> itemArray = new List<object>();
                                for (int j = 0; j < tempDT.Columns.Count; j++)//解决Excel超出DataTable列问题    lqwvje20181027
                                {
                                    itemArray.Add(GetValueType(temp_row.GetCell(j)));//解决 导入Excel  时间格式问题  lqwvje 20180904
                                }
                                tempDT.Rows.Add(itemArray.ToArray());
                            }
                        }
                        ds.Tables.Add(tempDT);
                    }
                }
                return ds;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        /// <summary>
        /// 将DataTable数据导入到excel中
        /// </summary>
        /// <param name="data">要导入的数据</param>
        /// <param name="isColumnWritten">DataTable的列名是否要导入</param>
        /// <param name="sheetName">要导入的excel的sheet的名称</param>
        /// <param name="fileName">导出的文件途径</param>
        /// <returns>导入数据行数(包含列名那一行)</returns>
        public static int DataTableToExcel(DataTable data, string sheetName, string fileName, bool isColumnWritten = true)
        {
            IWorkbook workbook = null;
            using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                if (fileName.IndexOf(".xlsx") > 0) // 2007版本
                {
                    workbook = new XSSFWorkbook();
                }
                else if (fileName.IndexOf(".xls") > 0) // 2003版本
                {
                    workbook = new HSSFWorkbook();
                }
                if (workbook == null) { return -1; }
                try
                {
                    ISheet sheet = workbook.CreateSheet(sheetName);
                    int count = 0;
                    if (isColumnWritten) //写入DataTable的列名
                    {
                        IRow row = sheet.CreateRow(0);
                        for (int j = 0; j < data.Columns.Count; ++j)
                        {
                            row.CreateCell(j).SetCellValue(data.Columns[j].ColumnName);
                        }
                        count = 1;
                    }
                    for (int i = 0; i < data.Rows.Count; ++i)
                    {
                        IRow row = sheet.CreateRow(count);
                        for (int j = 0; j < data.Columns.Count; ++j)
                        {
                            row.CreateCell(j).SetCellValue(data.Rows[i][j].ToString());
                        }
                        count++;
                    }
                    workbook.Write(fs,true); //写入到excel
                    return count;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception: " + ex.Message);
                    return -1;
                }
            }
        }
        /// <summary>
        /// 将DataSet数据导入到excel中   每个datatable一个sheet,sheet名为datatable名
        /// </summary>
        /// <param name="ds">要导入的数据</param>
        /// <param name="isColumnWritten">DataTable的列名是否要导入</param>
        /// <param name="fileName">导出的文件途径</param>
        public static bool DataTableToExcel(DataSet ds, string fileName, bool isColumnWritten = true)
        {
            if (ds == null || ds.Tables.Count < 1)
            {
                return false;
            }
            IWorkbook workbook = null;
            using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                if (fileName.IndexOf(".xlsx") > 0) // 2007版本
                {
                    workbook = new XSSFWorkbook();
                }
                else if (fileName.IndexOf(".xls") > 0) // 2003版本
                {
                    workbook = new HSSFWorkbook();
                }
                if (workbook == null) { return false; }
                try
                {
                    foreach (DataTable dt in ds.Tables)
                    {
                        ISheet sheet = workbook.CreateSheet(dt.TableName);
                        if (isColumnWritten) //写入DataTable的列名
                        {
                            IRow row = sheet.CreateRow(0);
                            for (int j = 0; j < dt.Columns.Count; ++j)
                            {
                                row.CreateCell(j).SetCellValue(dt.Columns[j].ColumnName);
                            }
                        }
                        for (int i = 0; i < dt.Rows.Count; ++i)
                        {
                            IRow row = sheet.CreateRow(isColumnWritten ? i + 1 : i);
                            for (int j = 0; j < dt.Columns.Count; ++j)
                            {
                                row.CreateCell(j).SetCellValue(dt.Rows[i][j].ToString());
                            }
                        }
                    }
                    workbook.Write(fs,true); //写入到excel
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception: " + ex.Message);
                    return false;
                }
            }
            return true;
        }
        private static DataTable BuildDataTable(IRow Row)
        {
            DataTable result = null;
            if (Row.Cells.Count > 0)
            {
                result = new DataTable();
                for (int i = 0; i < Row.LastCellNum; i++)
                {
                    if (Row.GetCell(i) != null)
                    {
                        result.Columns.Add(Row.GetCell(i).ToString());
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 获取单元格类型
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        private static object GetValueType(ICell cell)
        {
            if (cell == null)
                return null;
            switch (cell.CellType)
            {
                case CellType.Blank: //BLANK:  
                    return null;
                case CellType.Boolean: //BOOLEAN:  
                    return cell.BooleanCellValue;
                case CellType.Numeric: //NUMERIC:  
                    if (DateUtil.IsCellDateFormatted(cell))
                    {
                        return cell.DateCellValue;
                    }
                    return cell.NumericCellValue;
                case CellType.String: //STRING:  
                    return cell.StringCellValue;
                case CellType.Error: //ERROR:  
                    return cell.ErrorCellValue;
                case CellType.Formula: //FORMULA:  
                    cell.SetCellType(CellType.String);
                    return cell.StringCellValue;
                default:
                    return "=" + cell.CellFormula;
            }
        }
    }


在winform的中某触发事件中代码,调用刚刚的ExcelHelper对象,(这里还可以返回一个DataTable对象)


private void ExcelFile()
        {
            OpenFileDialog openFile = new OpenFileDialog();
            if (openFile.ShowDialog() == DialogResult.OK)
            {
                string filePath = openFile.FileName;
//获取本地的Excel文件
                DataTable excelDt = ExcelHelper.ReadFromExcel(filePath);
            }
        }

需要注意的是,在导入的时候如果有空白行的话,会把空白行也插入到数据库中,所以在读取本地的Excel数据之后,需要把空白行也去除掉。


List<DataRow> removelist = new List<DataRow>();
                    for (int i = 0; i < excelDt.Rows.Count; i++)
                    {
                        bool IsNull = true;
                        for (int j = 0; j < excelDt.Columns.Count; j++)
                        {
                            if (!string.IsNullOrEmpty(excelDt.Rows[i][j].ToString().Trim()))
                            {
                                IsNull = false;
                            }
                        }
                        if (IsNull)
                        {
                            removelist.Add(excelDt.Rows[i]);
                        }
                    }
                    for (int i = 0; i < removelist.Count; i++)
                    {
                        excelDt.Rows.Remove(removelist[i]);
                    }


到这里以上的操作步骤是获取本地的Excel数据了,那么接下来就可以去写入数据库中,需要注意的是:读取的数据,需要和数据库中的字段一一对应,也就是位置、名称都要对应上才行。


开发中遇到的问题:


1、用户需要提供的数据和数据库少于数据库中的字段,比如:用户需要提供姓名、手机号这两个字段,但是数据库中还需要有地址这个字段,那怎么办呢?我们可以在代码中给把这些信息补全。再写入数据库中。


示例代码


ProductTable.Columns.Add("creator");
                        ProductTable.Columns["creator"].SetOrdinal(26);//在第26列插入字段
ProductTable.Columns.Add("create_time");
                        ProductTable.Columns["create_time"].SetOrdinal(27);

2、在写数据库时,遇到数据类型不匹配。因为从本地获取的Excel数据基本上是string类型,那么数据库中有写字段是bool类型或者其他,如果插库的话就会报错,我这里解决这个问题的思路大概是在代码中把类型给转换一下,这样插入数据库时就不会出错了。


解决思路:比较笨的办法是把获取本地的数据(存放到了DataTable对象中)克隆出来一份,在克隆出来的那份中进行数据转换,因为在原来的数据不能进行转换,而且这里克隆不会把原来的数据进行克隆,所以在完成数据类型转换之后,还要把旧表中的数据同步到克隆出来的表中。


示例代码:


表克隆:


DataTable NewTable = new DataTable();
                        NewTable = ProductTable.Clone();//把原来的表进行克隆

修改类型:


//把新表中的表结构进行修改数据类型,和数据库中表字段进行保持一致
                        foreach (DataColumn col in NewTable.Columns)
                        {
                            #region ifelse
                            if (col.ColumnName == "conveyor_sel")
                            {
                                col.DataType = typeof(int);
                            }
                            else if (col.ColumnName == "p_length")
                            {
                                col.DataType = typeof(int);
                            }
                            else if (col.ColumnName == "p_width")
                            {
                                col.DataType = typeof(int);
                            }
}

旧表中的数据同步到新表中


foreach (DataRow item in ProductTable.Rows)
                            {
                                DataRow NewDtRow = NewTable.NewRow();
                                //获取对应行的产品代码的值
                                data = item.ItemArray[0];
                                NewDtRow["recipe"] = item["recipe"].ToString();
                                NewDtRow["p_name"] = item["p_name"].ToString();
                                NewDtRow["cust_name"] = item["cust_name"].ToString();
                                NewDtRow["cust_abbr"] = item["cust_abbr"].ToString();
                                NewDtRow["cust_field"] = item["cust_field"].ToString();
}


3、读取到本地Excel表中的的列标题是中文,但是数据库的形式是英文。这里也是用代码转换


示例代码:


ProductTable.Columns["产品代码"].ColumnName = "recipe";
                        ProductTable.Columns["产品名称"].ColumnName = "p_name";


使用SqlBulkCopy,把DataTable的数据写入数据库


这里用的EF框架。


public long AddDataTable(DataTable dt, String TableName)
        {
            PmsTestEntities db = new PmsTestEntities();
            SqlBulkCopy copy = new SqlBulkCopy(db.Database.Connection.ConnectionString, SqlBulkCopyOptions.UseInternalTransaction);//把本地文件复制
            copy.BatchSize = 100;//每次传输行数
            copy.NotifyAfter = dt.Rows.Count;//传输多少行后提示
            copy.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
            copy.DestinationTableName = TableName;//表名
            copy.WriteToServer(dt);
            return DataIncoming;
        }
//显示传入了多少的数据
private void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
        {
            long count = e.RowsCopied;
            DataIncoming = count;
        }

相关文章
|
7月前
|
SQL 关系型数据库 MySQL
阿里云RDS云数据库全解析:产品功能、收费标准与活动参考
与云服务器ECS一样,关系型数据库RDS也是很多用户上云必买的热门云产品之一,阿里云的云数据库RDS主要包含RDS MySQL、RDS SQL Server、RDS PostgreSQL、RDS MariaDB等几个关系型数据库,并且提供了容灾、备份、恢复、监控、迁移等方面的全套解决方案,帮助您解决数据库运维的烦恼。本文为大家介绍阿里云的云数据库 RDS主要产品及计费方式、收费标准以及活动等相关情况,以供参考。
|
10月前
|
SQL 存储 关系型数据库
MySQL功能模块探秘:数据库世界的奇妙之旅
]带你轻松愉快地探索MySQL 8.4.5的核心功能模块,从SQL引擎到存储引擎,从复制机制到插件系统,让你在欢声笑语中掌握数据库的精髓!
315 26
|
12月前
|
存储 缓存 自然语言处理
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
363 8
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
|
SQL Linux 数据库
【YashanDB知识库】崖山数据库Outline功能验证
本文来自YashanDB官网,主要测试了数据库优化器在不同场景下优先使用outline计划的功能。测试环境包括相同版本新增数据、绑定参数执行、单机主备架构以及数据库版本升级等场景。通过创建表、插入数据、收集统计信息和创建outline等步骤,验证了在各种情况下优化器均能优先采用存储的outline计划。测试结果表明,即使统计信息失效或数据库版本升级,outline功能依然稳定有效,确保查询计划的一致性和性能优化。详情可见[原文链接](https://www.yashandb.com/newsinfo/7488286.html?templateId=1718516)。
【YashanDB知识库】崖山数据库Outline功能验证
|
NoSQL 关系型数据库 MongoDB
Apifox与Apipost数据库连接功能详细对比,让接口管理更高效!
在现代软件开发中,数据库是应用运行的核心组件,接口管理工具则是连接和调试数据库的重要桥梁。本文对比了 Apifox 和 Apipost 两款工具的数据库连接功能。Apipost 支持全面的关系型与非关系型数据库(如 MySQL、Redis、MongoDB),功能强大且免费,适合复杂项目;而 Apifox 在关系型数据库支持上表现良好,但非关系型数据库(尤其是 Redis)功能有限且收费,更适合中小项目以关系型数据库为主的需求。根据项目需求选择合适的工具,可显著提升开发效率和稳定性。
|
10月前
|
Python
如何根据Excel某列数据为依据分成一个新的工作表
在处理Excel数据时,我们常需要根据列值将数据分到不同的工作表或文件中。本文通过Python和VBA两种方法实现该操作:使用Python的`pandas`库按年级拆分为多个文件,再通过VBA宏按班级生成新的工作表,帮助高效整理复杂数据。
|
10月前
|
数据采集 数据可视化 数据挖掘
用 Excel+Power Query 做电商数据分析:从 “每天加班整理数据” 到 “一键生成报表” 的配置教程
在电商运营中,数据是增长的关键驱动力。然而,传统的手工数据处理方式效率低下,耗费大量时间且易出错。本文介绍如何利用 Excel 中的 Power Query 工具,自动化完成电商数据的采集、清洗与分析,大幅提升数据处理效率。通过某美妆电商的实战案例,详细拆解从多平台数据整合到可视化报表生成的全流程,帮助电商从业者摆脱繁琐操作,聚焦业务增长,实现数据驱动的高效运营。
|
12月前
|
存储 安全 大数据
网安工程师必看!AiPy解决fscan扫描数据整理难题—多种信息快速分拣+Excel结构化存储方案
作为一名安全测试工程师,分析fscan扫描结果曾是繁琐的手动活:从海量日志中提取开放端口、漏洞信息和主机数据,耗时又易错。但现在,借助AiPy开发的GUI解析工具,只需喝杯奶茶的时间,即可将[PORT]、[SERVICE]、[VULN]、[HOST]等关键信息智能分类,并生成三份清晰的Excel报表。告别手动整理,大幅提升效率!在安全行业,工具党正碾压手动党。掌握AiPy,把时间留给真正的攻防实战!官网链接:https://www.aipyaipy.com,解锁更多用法!
|
数据采集 数据可视化 数据挖掘
利用Python自动化处理Excel数据:从基础到进阶####
本文旨在为读者提供一个全面的指南,通过Python编程语言实现Excel数据的自动化处理。无论你是初学者还是有经验的开发者,本文都将帮助你掌握Pandas和openpyxl这两个强大的库,从而提升数据处理的效率和准确性。我们将从环境设置开始,逐步深入到数据读取、清洗、分析和可视化等各个环节,最终实现一个实际的自动化项目案例。 ####
2591 10
|
数据采集 存储 JavaScript
自动化数据处理:使用Selenium与Excel打造的数据爬取管道
本文介绍了一种使用Selenium和Excel结合代理IP技术从WIPO品牌数据库(branddb.wipo.int)自动化爬取专利信息的方法。通过Selenium模拟用户操作,处理JavaScript动态加载页面,利用代理IP避免IP封禁,确保数据爬取稳定性和隐私性。爬取的数据将存储在Excel中,便于后续分析。此外,文章还详细介绍了Selenium的基本设置、代理IP配置及使用技巧,并探讨了未来可能采用的更多防反爬策略,以提升爬虫效率和稳定性。
921 4