ASP .NET應用程序常常會有從Excel文件中讀取數(shù)據(jù)或?qū)?shù)據(jù)寫入Excel的需求。一般來講,在ASP .NET中讀寫Excel文件有幾種解決方案。
使用OLE DB
使用OLE DB,可以以查詢數(shù)據(jù)庫的方式來讀取Excel文件,因為在某種程度上,Excel表格可以看成是一張一張的數(shù)據(jù)表。二者的主要區(qū)別在于所使用的數(shù)據(jù)引擎不一樣。使用OLE DB訪問Excel的要點是計算機上必須具有Microsoft Access Data Component 2.6(MADC2.6)以上版本,同時在連接字符串上必須聲明“Extended Properties=Excel 8.0”,這里指定的Excel版本號如果高于8.0可能會出錯,所以一般來講必須使用Excel 8.0。
其他的寫法就和一般的訪問數(shù)據(jù)庫一樣了,打開連接,填充數(shù)據(jù)集,再關閉連接即可。例如下面的實現(xiàn)代碼:
//創(chuàng)建一個數(shù)據(jù)鏈接 string strCon="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\\\sample.xls;Extended Properties=Excel 8.0"; OleDbConnection myConn=new OleDbConnection(strCon); string strCom="SELECT*FROM\[Sheet1$\]"; myConn.Open(); //打開數(shù)據(jù)鏈接,得到一個數(shù)據(jù)集 OleDbDataAdapter myCommand=new OleDbDataAdapter(strCom,myConn); //創(chuàng)建一個DataSet對象 myDataSet=new DataSet(); //得到自己的DataSet對象 myCommand.Fill(myDataSet,"\[Sheet1$\]"); //關閉此數(shù)據(jù)鏈接 myConn.Close();
|
使用這種解決方案的優(yōu)點是不需要其他的服務器組件,部署非常方便,但是缺點也是明顯的,用它來讀取Excel 2003格式以上的文件,會存在數(shù)據(jù)丟失的情況,而且也無法生成Excel文件。
使用Office主互操作程序集
另外一種解決方案是使用Office主互操作程序集,采用這種方式需要在服務器上安裝Office 2003,但是卻能夠比較精細地控制Excel文件的方方面面,包括格式、字體、顏色等。
下面的代碼演示了如何讀取Excel文件中某個單元格的值:
string excelFilePath=@"D:\\Book1.xls"; Excel.Application myExcel=new Excel.ApplicationClass(); object oMissing=System .Reflection.Missing.Value; myExcel.Application.Workbooks.Open(excelFilePath,oMissing, oMissing,oMissing,oMissing,oMissing, oMissing,oMissing,oMissing,oMissing,oMissing,oMissing,oMissing,oMissing,oMissing); Excel.Workbook myBook=myExcel.Workbooks\[1\]; Excel.Worksheet mySheet=(Excel.Worksheet)myBook.Worksheets\[1\]; System .Data.DataTable dt=new System .Data.DataTable("mytable"); dt.Columns.Add("F1",System .Type.GetType("System .String")); dt.Columns.Add("F2",System .Type.GetType("System .String")); dt.Columns.Add("F3",System .Type.GetType("System .String")); dt.Columns.Add("F4",System .Type.GetType("System .String")); dt.Columns.Add("F5",System .Type.GetType("System .String")); DataSet myDs=new DataSet(); myDs.Tables.Add(dt); DataRow myRow; myDs.Clear(); for(int i=2;i<=4;i++)//第一行為標題,不讀取 { myRow=myDs.Tables\["mytable"\].NewRow(); for(int j=1;j<=5;j++) { Excel.Range r=(Excel.Range)mySheet.Cells\[i,j\]; string strValue=r.Text.ToString(); string aa=strValue; string columnname="F"+j.ToString(); myRow\[columnname\]=strValue; } myDs.Tables\["mytable"\].Rows.Add(myRow); } DataGrid1.DataSource=myDs.Tables\["mytable"\].DefaultView; DataGrid1.DataBind(); 下面的代碼則演示了如何生成Excel文件,并向其中寫入值: string filename=""; Excel.ApplicationClass oExcel; oExcel=new Excel.ApplicationClass(); oExcel.UserControl=false; Excel.WorkbookClass b=(Excel.WorkbookClass)oExcel.Workbooks.Add(System .Reflection.Missing. Value); for(int i=1;i<=5;i++) { oExcel.Cells\[i,1\]=i.ToString(); oExcel.Cells\[i,2\]=""第2列"; oExcel.Cells\[i,3\]=""第3列"; oExcel.Cells\[i,4\]=""第4列"; } wb.Saved=true; filename=Request.PhysicalApplicationPath+"test.xls"; oExcel.ActiveWorkbook.SaveCopyAs(filename); oExcel.Quit(); System .GC.Collect(); Response.Redirect(Request.ApplicationPath+"/test.xls");
|
實際上,對于ASP .NET來講,這并不是一個很好的解決方案,原因是這個解決方案將客戶端的組件用到了服務器上,這往往會帶來一些意想不到的問題,如果在處理一個Excel文件的時候出錯,那么整個線程就會死在那里,服務器上的Excel進程無法重啟動,Excel文件無法刪除,后面的Excel文件也無法處理,只能重新啟動服務器,所以這是一個很大的問題。
使用ServerDocument
在上一個解決方案中,使用Office主互操作程序集存在很大的問題,究其原因是因為將本應在客戶端使用的Office組件用在了服務器上,這些Office組件在設計之初就沒有考慮在服務中調(diào)用的情況。對于在服務器上使用Excel文件,微軟推薦的解決方案是使用ServerDocument。
在具有Microsoft Visual Studio 2005 Tools for the Microsoft Office System自定義的Microsoft Office Word 2003文檔或Microsoft Office Excel 2003工作簿中,可以將數(shù)據(jù)存儲在嵌入的數(shù)據(jù)島中,無需啟動Excel或Word即可訪問數(shù)據(jù)島。
數(shù)據(jù)島是一個XML文檔,其中包含Office文檔中嵌入的數(shù)據(jù),無需實例化Office文檔即可訪問該XML文檔。數(shù)據(jù)實際存在于兩個地方,即文檔和單獨的嵌入數(shù)據(jù)島中。在數(shù)據(jù)島與文檔之間使用了數(shù)據(jù)綁定,以使它們保持同步。如果服務器上運行的代碼修改了數(shù)據(jù)島,則在文檔打開,并且文檔中的代碼運行時,Office文檔會與數(shù)據(jù)島進行同步。
此模型具有以下幾項優(yōu)勢:
·可以將驗證代碼添加到獨立于文檔的數(shù)據(jù)中。通過將驗證與文檔分離,可以將數(shù)據(jù)驗證代碼移植到其他文檔中。
·數(shù)據(jù)島使用可脫機使用的數(shù)據(jù)填充。當文檔中的緩存數(shù)據(jù)項包含數(shù)據(jù)時,該文檔將與數(shù)據(jù)島進行交互。
·由于可以從外部訪問數(shù)據(jù)島,因此無需實例化Office就可以修改文檔中嵌入的數(shù)據(jù),從而支持服務器上文檔的快速批處理。但是,只能訪問緩存中的數(shù)據(jù),而不是文檔中的所有數(shù)據(jù)。
下面的代碼演示了如何使用ServerDocument訪問數(shù)據(jù):
string expenseDoc=@"C:\\ExpenseDocuments\\Expenses0105.xls"; ServerDocument sd1=null; Try { sd1=new ServerDocument(expenseDoc); CachedDataHostItem dataHostItem1= sd1.CachedData.HostItems\["DataNamespace.DataWorksheet"\]; CachedDataItem dataItem1=dataHostItem1.CachedData\["DataCache"\]; System .IO.StringReader schemaReader=new System .IO.StringReader(dataItem1.Schema); System .IO.StringReader xmlReader=new System .IO.StringReader(dataItem1.Xml); ExpenseData.ReadXmlSchema(schemaReader); ExpenseData.ReadXml(xmlReader); } Finally { if(sd1!=null) { sd1.Close(); } } 下面的代碼演示了如何使用ServerDocument從數(shù)據(jù)生成Excel文檔: string name=@"C:\\Documents\\WordApplication3.doc"; System .IO.FileStream fileStream=null; byte\[\]bytes=null; try { fileStream=new System .IO.FileStream( name,System .IO.FileMode.Open,System .IO.FileAccess.Read); bytes=new byte\[(int)fileStream .Length\]; fileStream .Read(bytes,0,(int)fileStream .Length); } Finally { if(fileStream!=null) { fileStream .Close(); } } ServerDocument sd1=null; Try { sd1=new ServerDocument(bytes,name); //這里是數(shù)據(jù)操作的代碼 sd1.Save(); bytes=sd1.Document; //如果是一個Word文檔,使用如下的MIME字符串 Response.ContentType="application/msword"; //如果是一個Excel工作簿,使用如下的MIME字符串 //Response.ContentType="application/vnd.ms-excel"; Response.AddHeader("Content-disposition","filename="+name); Response.Write(sd1); } Finally { if(sd1!=null) { sd1.Close(); } }
|
相對來說,這是一個比較理想的解決方案,但是使用該方案有如下幾個限制:
·客戶端必須安裝.NET Framework 2.0、Office Tools for Visual Studio和Office 2003。
·開發(fā)嵌入于文檔中的程序集,用于同步視圖和數(shù)據(jù)島中的數(shù)據(jù),例如用數(shù)據(jù)島數(shù)據(jù)中的某個值更新某單元格中的值,或者用某單元格中的值更新數(shù)據(jù)島中的值。
·使用專用的Excel模板。
對于Office 2007的OpenXML格式使用OpenXML SDK
如果確定ASP .NET應用程序僅處理Office 2007生成的OpenXML格式文檔,那么使用OpenXML SDK是一個更好的主意,因為它不需要在服務器上安裝Office,對客戶端也沒有任何要求,只要用的是OpenXML格式的文檔就可以了。因為OpenXML格式已提交國際標準化組織,所以,用戶可以使用支持OpenXML的任何應用程序讀取和編輯該文檔。
使用該方案需要下載OpenXML SDK,并使用其進行開發(fā),目前該SDK還只是一個CTP版本。下面的代碼演示了如何使用OpenXML SDK讀寫OpenXML格式的Excel文件:
//怎樣獲取WorkSheet信息 public List<string>XLGetSheetInfo(string fileName) { //用所有的sheet添充這個集合 List<string>sheets=new List<string>(); using(SpreadsheetDocument xlPackage=SpreadsheetDocument.Open(fileName,false)) { WorkbookPart workbook=xlPackage.WorkbookPart; Stream workbookstr=workbook.GetStream(); XmlDocument doc=new XmlDocument(); doc.Load(workbookstr); XmlNamespaceManager nsManager=new XmlNamespaceManager(doc.NameTable); nsManager.AddNamespace("default",doc.DocumentElement.NamespaceURI); XmlNodeList nodelist=doc.SelectNodes("http://default:sheets/default:sheet",nsManager); foreach(XmlNode node in nodelist) { string sheetName=string.Empty; sheetName=node.Attributes\["name"\].Value; sheets.Add(sheetName); } } return sheets; } //怎樣為一個Excel文檔創(chuàng)建一個新的包 public static void CreateNewExcelDocument(string document) { using(SpreadsheetDocument xcelDoc=SpreadsheetDocument.Create(document,preadsheetDocumentType.Workbook)) { //設置文檔內(nèi)存以便Excel能夠打開 WorkbookPart mainPart=excelDoc.AddWorkbookPart(); SetMainDocumentContent(mainPart); } } //設置主文檔部分內(nèi)存 public static void SetMainDocumentContent(WorkbookPart part,string excelXml) { using(Stream stream=part.GetStream()) { byte\[\]buf=(new UTF8Encoding()).GetBytes(docXml); stream .Write(buf,0,buf.Length); } }
|
【責