隐藏

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

发布:2024/1/26 19:27:51作者:管理员 来源:本站 浏览次数:763

背景


最近在项目中有一个导入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找到,下载并安装。


这里可能出现问题是


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




安装好之后,再去下载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;

           }