DataSet导出到Excel比较完整的解决方案(二)服务器端生成文件(downmoon)

前一篇文章中,介绍了DataSet导出到Excel时客户端生成文件的几种思路,接着往下说,服务器端生成文件,用户直接下载,应该格式是可以保证的!

于是直接调用Excel的API生成。代码如下:


 public static void DataSetToLocalExcel(DataSet dataSet, string outputPath, bool deleteOldFile)
        
{
            
if (deleteOldFile)
            
{
                
if (System.IO.File.Exists(outputPath)) { System.IO.File.Delete(outputPath); }
            }

            
// Create the Excel Application object
            ApplicationClass excelApp = new ApplicationClass();

            
// Create a new Excel Workbook
            Workbook excelWorkbook = excelApp.Workbooks.Add(Type.Missing);

            
int sheetIndex = 0;

            
// Copy each DataTable
            foreach (System.Data.DataTable dt in dataSet.Tables)
            
{

                
// Copy the DataTable to an object array
                object[,] rawData = new object[dt.Rows.Count + 1, dt.Columns.Count];

                
// Copy the column names to the first row of the object array
                for (int col = 0; col < dt.Columns.Count; col++)
                
{
                    rawData[
0, col] = dt.Columns[col].ColumnName;
                }


                
// Copy the values to the object array
                for (int col = 0; col < dt.Columns.Count; col++)
                
{
                    
for (int row = 0; row < dt.Rows.Count; row++)
                    
{
                        rawData[row 
+ 1, col] = dt.Rows[row].ItemArray[col];
                    }

                }


                
// Calculate the final column letter
                string finalColLetter = string.Empty;
                
string colCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
                
int colCharsetLen = colCharset.Length;

                
if (dt.Columns.Count > colCharsetLen)
                
{
                    finalColLetter 
= colCharset.Substring(
                        (dt.Columns.Count 
- 1/ colCharsetLen - 11);
                }


                finalColLetter 
+= colCharset.Substring(
                        (dt.Columns.Count 
- 1% colCharsetLen, 1);

                
// Create a new Sheet
                Worksheet excelSheet = (Worksheet)excelWorkbook.Sheets.Add(
                    excelWorkbook.Sheets.get_Item(
++sheetIndex),
                    Type.Missing, 
1, XlSheetType.xlWorksheet);

                excelSheet.Name 
= dt.TableName;

                
// Fast data export to Excel
                string excelRange = string.Format("A1:{0}{1}",
                    finalColLetter, dt.Rows.Count 
+ 1);

                excelSheet.get_Range(excelRange, Type.Missing).Value2 
= rawData;

                
// Mark the first row as BOLD
                ((Range)excelSheet.Rows[1, Type.Missing]).Font.Bold = true;
            }

            
//excelApp.Application.AlertBeforeOverwriting = false;
            excelApp.Application.DisplayAlerts = false;
            
// Save and Close the Workbook
            excelWorkbook.SaveAs(outputPath, XlFileFormat.xlWorkbookNormal, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlExclusive,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
            excelWorkbook.Close(
true, Type.Missing, Type.Missing);
            excelWorkbook 
= null;

            
// Release the Application object
            excelApp.Quit();
            excelApp 
= null;

            
// Collect the unreferenced objects
            GC.Collect();
            GC.WaitForPendingFinalizers();

        }

说明下,其中的  xlsApp.Application.DisplayAlerts   =   false;  的作用是不显示确认对话框    

也可以逐Cell读取,那样可能会慢。本方法速度还过得去。

生成Winform代码测试没错,部署时,以为只要引用两个dll就可以了

Microsoft.Office.Interop.Excel.dll

Office.dll


那成想,问题接着来了,当在WebForm下调用时, 提示“检索   COM   类工厂中   CLSID   为   {00024500-0000-0000-C000-000000000046}   的组件时失败,原因是出现以下错误:   8000401a

晕! Google下,解决方案是在服务器上安装Office,并配置DCOM权限。步骤如下:


配置  DCOM  中  EXCEL  应用程序:
要在交互式用户帐户下设置  Office  自动化服务器,请按照下列步骤操作: 
1.  以管理员身份登录到计算机,并使用完整安装来安装(或重新安装)Office。为了实现系统的可靠性,建议您将  Office  CD-ROM  中的内容复制到本地驱动器并从此位置安装  Office。 
2.  启动要自动运行的  Office  应用程序。这会强制该应用程序进行自我注册。 
3.  运行该应用程序后,请按  Alt+F11  以加载  Microsoft  Visual  Basic  for  Applications  (VBA)  编辑器。这会强制  VBA  进行初始化。 
4.  关闭应用程序,包括  VBA。 
5.  单击开始,单击运行,然后键入  DCOMCNFG。选择要自动运行的应用程序。应用程序名称如下所示: 
Microsoft  Access  97  -  Microsoft  Access  数据库
Microsoft  Access  2000/2002  -  Microsoft  Access  应用程序
Microsoft  Excel  97/2000/2002  -  Microsoft  Excel  应用程序
Microsoft  Word  97  -  Microsoft  Word  Basic
Microsoft  Word  2000/2002  -  Microsoft  Word  文档 
单击属性打开此应用程序的属性对话框。
6.  单击安全选项卡。验证使用默认的访问权限和使用默认的启动权限已选中。 
7.  单击标识选项卡,然后选择交互式用户。 
8.  单击确定,关闭属性对话框并返回主应用程序列表对话框。 
9.  在  DCOM  配置对话框中,单击默认安全性选项卡。 
10.  单击访问权限的编辑默认值。验证访问权限中是否列出下列用户,如果没有列出,则添加这些用户: 
SYSTEM
INTERACTIVE
Everyone
Administrators
IUSR_ 
<machinename> *
IWAM_ 
<machinename> *
*  这些帐户仅在计算机上安装了  Internet  Information  Server  (IIS)  的情况下才存在。 
11.  确保允许每个用户访问,然后单击确定。 
12.  单击启动权限的编辑默认值。验证启动权限中是否列出下列用户,如果没有列出,则添加这些用户: 
SYSTEM
INTERACTIVE
Everyone
Administrators
IUSR_ 
<machinename> *
IWAM_ 
<machinename> *
*  这些帐户仅在计算机上安装有  IIS  的情况下才存在。 
13.  确保允许每个用户访问,然后单击确定。 
14.  单击确定关闭  DCOMCNFG。 
如果你之前起用了身份模拟  (在  web.config  中配置了  
<identity  impersonate= "true "/>  )  ,需要删除之! 
15.更新安装office,把.net可编程组件安装到本机(excel组件)
如果还是不行.干脃把交互式用户 换成"启动用户" 

 
折腾了一番,总算可以用了!·只是服务器上装Office总感觉不爽,于是再尝试下别的方法:

Reading and Writing Excel using OLEDB

主要的类文件如下:

 /// <summary>
    
/// Summary description for ExcelReader.
    
/// </summary>

    public class ExcelReader : IDisposable
    
{
        
Variables

        
properties

        
Methods

        
public

        
Dispose / Destructor

        
CTOR
    }

思路:通过读出Excel模板文件到DataTale,再把数据填充到DataTable,文件另存下就OK了!

 调用代码如下:


 public static string path = @"TempExcel\STemp.xls";
        
public static string path2 = "TestUser.xls";
        
public static string PreFilePath = @"C:\Excel\";
        
public static void DataSetToLocalExcel(DataSet ds, string srcPath, string outputPath, bool deleteOldFile)
        
{
            
if (ds == null || ds.Tables[0== null && ds.Tables[0].Rows.Count == 0return; }
            
if (deleteOldFile)
            
{
                
if (System.IO.File.Exists(outputPath)) { System.IO.File.Delete(outputPath); }
            }

            System.IO.File.Copy(srcPath, outputPath, 
true);
            ExcelReader exr 
= new ExcelReader();
            exr.ExcelFilename 
= outputPath;
            exr.Headers 
= true;
            exr.MixedData 
= true;
            exr.KeepConnectionOpen 
= true;
            
string[] sheetnames = exr.GetExcelSheetNames();
            exr.SheetName 
= sheetnames[0];
            DataTable dt 
= exr.GetTable();
            
if (dt == nullreturn;
            exr.SetPrimaryKey(
0);
            
//dt.PrimaryKey = new DataColumn[] { dt.Columns["编号"] };
            DataTable dt2 = ds.Tables[0].Copy();
            dt.Rows.Clear();
            
for (int i = 0; i < dt2.Rows.Count; i++)
            
// Copy the values to the object array
                DataRow dr = dt.NewRow();
                
for (int col = 0; col < dt.Columns.Count; col++)
                
{
                    dr[col] 
= dt2.Rows[i][col];
                }

                dt.Rows.Add(dr);
            }

            exr.SetTable(dt);
            
#region WriteFile
           
            
#endregion

            exr.Close();
            exr.Dispose();
            exr 
= null;
        }

        
private DataSet Get_AllPrices()
        
{
            
try
            
{
                
// Get the employee details
                string strSql = "SELECT [CustomID] as 编号,[C_Name] as 品名,0 as 最高价格,0 as 最低价格,0 as 平均价格,'元/公斤' as 计量单位,'' as 备注  FROM [PriceCategory] WHERE ( 1=1 AND ([Puser] = 'tuser') )";
                SqlConnection objConn 
= new SqlConnection(@"Data Source=AP6;Initial Catalog=testdb2009 ;Persist Security Info=True;User ID=sa;Password=sa");
                SqlDataAdapter daEmp 
= new SqlDataAdapter(strSql, objConn);
                daEmp.Fill(dsPrice, 
"price");
                
return dsPrice;
            }

            
catch (Exception Ex)
            
{
                
throw Ex;
            }

        }


        DataSet dsPrice 
= new DataSet();
        
protected void btnGetData_Click(object sender, EventArgs e)
        
{
            DataSetToLocalExcel(Get_AllPrices(), PreFilePath 
+ path, PreFilePath + path2, true);
        }

 

这里有点强调下:OleDbConnection特别要注意, 刚开始用http://www.connectionstrings.com/excel

  提供的标准串:

"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:"MyExcel.xls;Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";

结果提示:“操作必须使用一个可更新的查询”。因为读取结果正常,以为是excel没有写权限所致,增加了相应权限后,结果依然如故。这下火了! Google下, 有解决方案

http://www.cnblogs.com/richinger/archive/2008/09/28/1301170.html

A: HDR ( HeaDer Row )设置
    若指定值为Yes,代表 Excel 档中的工作表第一行是栏位名称

    若指定值為 No,代表 Excel 档中的工作表第一行就是資料了,沒有栏位名称

    B:IMEX ( IMport EXport mode )设置

     IMEX 有三种模式,各自引起的读写行为也不同,容後再述:
     0 is Export mode
     1 is Import mode
     2 is Linked mode (full update capabilities)

    

于是修改为:


Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:"MyExcel.xls;Extended Properties="Excel 8.0;HDR=Yes;IMEX=2";
原文地址:https://www.cnblogs.com/liufei88866/p/1376765.html