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

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 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;
        }

相关文章
|
3月前
|
C++ Windows
.NET Framework安装不成功,下载`NET Framework 3.5`文件,Microsoft Visual C++
.NET Framework常见问题及解决方案汇总,涵盖缺失组件、安装失败、错误代码等,提供多种修复方法,包括全能王DLL修复工具、微软官方运行库及命令行安装等,适用于Windows系统,解决应用程序无法运行问题。
271 3
|
19天前
|
开发框架 安全 .NET
Microsoft .NET Framework 3.5、4.5.2、4.8.1,适用于 Windows 版本的 .NET,Microsoft C Runtime等下载
.NET Framework是Windows平台的开发框架,包含CLR和FCL,支持多种语言开发桌面、Web应用。常用版本有3.5、4.5.2、4.8.1,系统可同时安装多个版本,确保软件兼容运行。
376 0
Microsoft .NET Framework 3.5、4.5.2、4.8.1,适用于 Windows 版本的 .NET,Microsoft C Runtime等下载
|
1月前
|
SQL 关系型数据库 MySQL
如何将Excel表的数据导入RDS MySQL数据库?
本文介绍如何通过数据管理服务DMS将Excel文件(转为CSV格式)导入RDS MySQL数据库,涵盖建表、编码设置、导入模式选择及审批执行流程,并提供操作示例与注意事项。
|
2月前
.NET Framework 3.5离线安装包合集下载
本文介绍了如何获取和安装.NET Framework运行库离线合集包。用户可通过提供的链接下载安装包,安装过程简单,按提示逐步操作即可完成。安装时可选择所需版本,工具会自动适配架构,无需手动判断,方便高效。
1112 0
|
8月前
|
SQL 关系型数据库 MySQL
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
352 9
|
7月前
|
SQL 容灾 关系型数据库
阿里云DTS踩坑经验分享系列|DTS打通SQL Server数据通道能力介绍
SQL Server 以其卓越的易用性和丰富的软件生态系统,在数据库行业中占据了显著的市场份额。作为一款商业数据库,外部厂商在通过解析原生日志实现增量数据捕获上面临很大的挑战,DTS 在 SQL Sever 数据通道上深研多年,提供了多种模式以实现 SQL Server 增量数据捕获。用户可以通过 DTS 数据传输服务,一键打破自建 SQL Server、RDS SQL Server、Azure、AWS等他云 SQL Server 数据孤岛,实现 SQL Server 数据源的流动。
395 0
阿里云DTS踩坑经验分享系列|DTS打通SQL Server数据通道能力介绍
|
7月前
|
存储 XML 开发工具
【Azure Storage Account】利用App Service作为反向代理, 并使用.NET Storage Account SDK实现上传/下载操作
本文介绍了如何在Azure上使用App Service作为反向代理,以自定义域名访问Storage Account。主要内容包括: 1. **设置反向代理**:通过配置`applicationhost.xdt`和`web.config`文件,启用IIS代理功能并设置重写规则。 2. **验证访问**:测试原生URL和自定义域名的访问效果,确保两者均可正常访问Storage Account。 3. **.NET SDK连接**:使用共享访问签名(SAS URL)初始化BlobServiceClient对象,实现通过自定义域名访问存储服务。
118 1
|
10月前
|
数据可视化 数据处理 Python
使用Pandas实现Excel中的数据透视表功能
本文介绍了如何使用Python的Pandas库实现Excel中的数据透视表功能,包括环境准备、创建模拟销售数据、代码实现及输出等步骤。通过具体示例展示了按地区和销售员汇总销售额的不同方法,如求和、平均值、最大值等,帮助读者掌握Pandas在数据处理上的强大能力。
338 12
|
11月前
|
SQL 存储 缓存
SQL Server 数据太多如何优化
11种优化方案供你参考,优化 SQL Server 数据库性能得从多个方面着手,包括硬件配置、数据库结构、查询优化、索引管理、分区分表、并行处理等。通过合理的索引、查询优化、数据分区等技术,可以在数据量增大时保持较好的性能。同时,定期进行数据库维护和清理,保证数据库高效运行。
308 4
|
1月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
84 3

热门文章

最新文章