2013年9月25日 星期三

ASP.NET C# EXCEL 檔案上傳不儲存檔案讀取資料

在以往的檔案上傳,都要先將上傳的檔案存到伺服器端的某個目錄,還必須要將檔案命名成獨立的名稱,不然同時有可能會發生有人檔案上傳失敗。

但是如果不存實體檔案,存到一個暫存空間 ( MemoryStream ),再將資料轉換成 DataTable 或者是 IDataReader ,最後資料讀取出來或傳入 GridView 使用。

1.

首先先下載 ClosedXML:http://closedxml.codeplex.com/releases/view/110822 並引入參考。

2.

撰寫一個類別使用 ClosedXML 寫常用的方法到時方便使用。 ( 以下是參考程式碼,可依照狀況不同調整 )
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.OleDb;
using System.IO;
using ClosedXML.Excel;

namespace FileHanding
{
    //ClosedXML Documentation: http://closedxml.codeplex.com/documentation
    public class FileHanding
    {
        public MemoryStream TransferDataTableToExcel(DataTable dt)
        {
            var wb = new XLWorkbook();
            wb.Worksheets.Add(dt);
            MemoryStream ms = new MemoryStream();
            wb.SaveAs(ms);
            return ms;
        }

        public DataTable TransferExcelToDataTable(byte[] file)  
        {
            Stream fileStream = new MemoryStream(file);
            var workbook = new XLWorkbook(fileStream);
            var xlWorksheet = workbook.Worksheet(1);
            return TransferExcelToDataTable(xlWorksheet);
        }

        public DataTable TransferExcelToDataTable(byte[] file, string sheetName)
        {
            Stream fileStream = new MemoryStream(file);
            var workbook = new XLWorkbook(fileStream);
            var xlWorksheet = workbook.Worksheet(sheetName);
            return TransferExcelToDataTable(xlWorksheet);
        }

        public DataTable TransferExcelToDataTable(string filePath)
        {
            var workbook = new XLWorkbook(filePath);
            var xlWorksheet = workbook.Worksheet(1);
            return TransferExcelToDataTable(xlWorksheet);
        }

        public DataTable TransferExcelToDataTable(string filePath, string sheetName)
        {
            var workbook = new XLWorkbook(filePath);
            var xlWorksheet = workbook.Worksheet(sheetName);
            return TransferExcelToDataTable(xlWorksheet);
        }

        private DataTable TransferExcelToDataTable(IXLWorksheet xlWorksheet)
        {
            var datatable = new DataTable();
            var range = xlWorksheet.Range(xlWorksheet.FirstCellUsed(), xlWorksheet.LastCellUsed());

            int col = range.ColumnCount();
            int row = range.RowCount();

            // add columns hedars
            datatable.Clear();

            for (int i = 1; i <= col; i++)
            {
                IXLCell column = xlWorksheet.Cell(1, i);
                datatable.Columns.Add(column.Value.ToString());
            }

            // add rows data   
            int firstHeadRow = 0;
            foreach (var item in range.Rows())
            {
                if (firstHeadRow != 0)
                {
                    var array = new object[col];
                    for (int y = 1; y <= col; y++)
                    {
                        array[y - 1] = item.Cell(y).Value;
                    }
                    datatable.Rows.Add(array);
                }
                firstHeadRow++;
            }
            return datatable;
        }

        
        public IDataReader TransferExcelToIDataReader(byte[] file)
        {
            Stream fileStream = new MemoryStream(file);
            var workbook = new XLWorkbook(fileStream);
            var xlWorksheet = workbook.Worksheet(1);
            return TransferExcelToIDataReader(xlWorksheet);
        }

        public IDataReader TransferExcelToIDataReader(byte[] file, string sheetName)
        {
            Stream fileStream = new MemoryStream(file);
            var workbook = new XLWorkbook(fileStream);
            var xlWorksheet = workbook.Worksheet(sheetName);
            return TransferExcelToIDataReader(xlWorksheet);
        }

        public IDataReader TransferExcelToIDataReader(string filePath)
        {
            var workbook = new XLWorkbook(filePath);
            var xlWorksheet = workbook.Worksheet(1);
            return TransferExcelToIDataReader(xlWorksheet);
        }

        public IDataReader TransferExcelToIDataReader(string filePath, string sheetName)
        {
            var workbook = new XLWorkbook(filePath);
            var xlWorksheet = workbook.Worksheet(sheetName);
            return TransferExcelToIDataReader(xlWorksheet);
        }

        private IDataReader TransferExcelToIDataReader(IXLWorksheet xlWorksheet)
        {
            var datatable = new DataTable();
            var range = xlWorksheet.Range(xlWorksheet.FirstCellUsed(), xlWorksheet.LastCellUsed());

            int col = range.ColumnCount();
            int row = range.RowCount();

            // add columns hedars
            datatable.Clear();

            for (int i = 1; i <= col; i++)
            {
                IXLCell column = xlWorksheet.Cell(1, i);
                datatable.Columns.Add(column.Value.ToString());
            }

            // add rows data   
            int firstHeadRow = 0;
            foreach (var item in range.Rows())
            {
                if (firstHeadRow != 0)
                {
                    var array = new object[col];
                    for (int y = 1; y <= col; y++)
                    {
                        array[y - 1] = item.Cell(y).Value;
                    }
                    datatable.Rows.Add(array);
                }
                firstHeadRow++;
            }
            return datatable.CreateDataReader();
        }



        public MemoryStream TransferDataTableToCsv(DataTable dt)
        {
            MemoryStream ms = new MemoryStream();
            StreamWriter result = new StreamWriter(ms, Encoding.UTF8);

            //Header
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                result.Write(dt.Columns[i].ColumnName);
                result.Write(i == dt.Columns.Count - 1 ? "\n" : ",");
            }

            //Content
            foreach (DataRow row in dt.Rows)
            {
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    result.Write(row[i].ToString());
                    result.Write(i == dt.Columns.Count - 1 ? "\n" : ",");
                }
            }
            
            return ms;
        }

        public DataTable TransferCsvToDataTable(string strFilePath)
        {
            string strFileName = Path.GetFileName(strFilePath);
            string strFileDirectory = Path.GetDirectoryName(strFilePath);
            string strConn = string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}\;Extended Properties='Text;HDR=Yes;'", strFileDirectory);
            string strSQL = string.Format("SELECT * FROM [{0}]", strFileName);
            OleDbDataAdapter adapter = new OleDbDataAdapter(strSQL, strConn);
            DataTable dt = new DataTable();
            adapter.Fill(dt);
            return dt;
        }

    }
}

3.

介面上,只需要用一般的 input 加上 runat="server" 即可,利用 Postback 將檔案讀取。以下是介面圖:

再看看程式碼快照:

4.

在上傳檔案時,將檔案轉換成 byte[],並傳入已經寫好的類別 ( FileHanding ) 方法進行轉換,以下為參考程式碼:
using FileHanding;

...
...
...
...
...
...

protected void btnImport_Click(object sender, EventArgs e)
{
    if (IsPostBack)
    {
        if (file.PostedFile != null)
        {
            FileHanding fh = new FileHanding();;
            string strErrorMessage = string.Empty;
            var postedFile = file.PostedFile;
            int iDataLength = postedFile.ContentLength;
            byte[] bData = new byte[iDataLength];
            bool bIsVerify = true;
            postedFile.InputStream.Read(bData, 0, iDataLength);

            if (txtSheetName.Text.Trim() == string.Empty)
                dt = fh.TransferExcelToDataTable(bData);
            else
                dt = fh.TransferExcelToDataTable(bData, txtSheetName.Text);

            /* 以下再做資料處理 */ 


        }
    }
}





2013年9月23日 星期一

如何在 .aspx 將資料 post 到 .ashx

最近小弟要做一個活動頁面,此頁面是投票活動,要綁會員投票資訊,但是未登入者也可以觀看此頁面。

使用者給我的介面有兩個步驟完成投票,第一個步驟有兩個區塊,一個是即時登入頁面,一個是即時註冊頁面;第二個步驟是投票選項,這兩個步驟都選定了才能將資料送出。

附帶條件是不能利用連結導向到登入頁面和註冊頁面,因為怕瀏覽者點走了就不知道怎麼回來,所以只能在當頁將所有動作完成。

遇到這個狀況,我想說如果使用 ajax 送出資料到另一個頁面去註冊或者做登入,那成功或錯誤訊息送回來,我要怎麼將傳回來的 MemberID 存起來,隱藏欄位?cookie?最後決定使用 post 到 ashx 再將 MemberID 傳回來,再將傳回來的資料存在session內,繼續做投票動作。

於是 我參考了 [ASP.NET]在server端post資料到.ashx 內的程式碼來解決問題。

LoginHander.ashx.cs
public class LoginHandler : IHttpHandler, IRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string strID = string.Empty;
        string strMsg = string.Empty;
        string strEMail = context.Request.Form["Email"];
        string strPassword = context.Request.Form["Password"];

        if (string.IsNullOrEmpty(strEMail))
        {
            strMsg += (strMsg == string.Empty) ? "Please input email address.<br/>" : "";
        }

        if (string.IsNullOrEmpty(strPassword))
        {
            strMsg += (strMsg == string.Empty) ? "Please input password.<br/>" : "";
        }
        
        //建立會員資料查詢物件
       
        string msg;

        bool isSuccess = /* 檢查帳密是否正確 */
        if (!isSuccess)
        {
            strMsg += (strMsg == string.Empty) ? "Sorry, unrecognized email address or password.<br/>" : "";
        }

  /* 額外判斷 */
  
  strID = /* 取得會員ID */
  
        if (string.IsNullOrWhiteSpace(strMsg))
        {
            context.Response.Write("true," + strID);

        }
        else
            context.Response.Write(strMsg);
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

.aspx.cs 中的按鈕
protected void btnSubmitLogin_Click(object sender, EventArgs e)
{
    lblLoginErrorMessage.Text = string.Empty;
    var url = "http://www.sample.com/../../LoginHandler.ashx"; // 網址路徑 

    WebRequest request = WebRequest.Create(url);
    request.Method = "POST";

    // server 端可以透過 Request.Form["Email"] 和 Request.Form["Password"] 讀取
    string postData = string.Format("Email={0}&Password={1}", txtEMail.Text, txtPassword.Text);

    byte[] byteArray = Encoding.UTF8.GetBytes(postData);

    request.ContentType = "application/x-www-form-urlencoded";

    // Set the ContentLength property of the WebRequest.
    request.ContentLength = byteArray.Length;

    // Get the request stream.
    using (Stream dataStream = request.GetRequestStream())
    {
        // Write the data to the request stream.
        dataStream.Write(byteArray, 0, byteArray.Length);
    }

    // Get the response.      
    using (WebResponse response = request.GetResponse())
    {
        using (Stream dataStream = response.GetResponseStream())
        {
            using (StreamReader reader = new StreamReader(dataStream))
            {
                string responseFromServer = reader.ReadToEnd();
    
    /* 回傳資料若開頭為 true */
                if (responseFromServer.Trim().StartsWith("true"))
                {
     /* 紀錄 ID */
                    Session["MemberID"] = responseFromServer.Trim().Replace("true,", "");

     /* 後續資料處理 */
                }
                else
                {
     /* 錯誤訊息顯示 */
                    lblLoginErrorMessage.Text = responseFromServer.Trim();
                }
            }
        }
    }
}





2013年9月11日 星期三

ASP.NET 中關於 Postback 與離開頁面的分辨



大家在使用Asp.net開發應用系統時,有時會存在這樣的場景,當頁面離開時需要清空Session等等一系列的後序操作。

如果大家使用後台代碼清空Session,那麼當頁面離開時是不會Postback頁面的,問題就出在這裡,那麼我們只能從前台入手了,可以使用前台事件window.onunload,沒有問題,我們繼續,當我們正常的載入Asp.Net頁面,點擊一個Button,頁面PostBack之前每次都會觸發onunload事件,那麼我們如何才能在前台利用onunload事件分析出在什麼情況下是postback,什麼情況下是離開呢?

    var isSubmit = false;
    $(document).ready(function () {
        document.forms[0].onsubmit = SubmitFun;
        window.onunload = CheckAction;
    });
 
    function SubmitFun()
    {
        isSubmit = true;
    }
 
    function CheckAction() {
        if (!isSubmit) {

            var guid = $('#<%=hfGuid.ClientID %>').val();
            
            // ajax 執行刪除某一系列 Session 的動作
            // $.get('<%=ResolveClientUrl("...")%>', function () { });
            
            alert("is leave");
            //to do 
            //for example use the ajax invoke service method to clean session 
        }
        else {
            alert("is submit");
            //to do
        }
    }

我們結合使用window.onunload與form.onsubmit兩個事件來完成分辨,當提交一個form時,觸發onsubmit事件,我們在這個事件處理方法中,將isSubmit置為true,在onunload事件中,根據isSubmit去分辨是提交頁面還是頁面離開,由於onunload事件總是在onsubmit事件之後執行,所以可以保證得到正確的結果。

 在註釋處我們可以添加自己想要的代碼,例如使用ajax調用後台方法等等...


引用:Web系统中关于Postback与页面离开的分辨