2012年5月17日 星期四

利用泛型處理常式匯出報表 - ashx, reporting service

報表為了保護機密內容,報表伺服器通常需要做身分驗證。
有些報表有時需要直接產生 PDF 檔供使用者下載,且不太希望使用者自行用介面儲存成其他格式,所以並不會利用 Reportviewer 這個控制項。
這時可利用泛型處理常式來當作中介,自動完成報表伺服器且自動下載 PDF 檔供使用者使用。



1.首先制方案總管,至網站底下按右鍵,點選 [加入新項目] 。

2.點選 [泛型處理常式] 。
3.因為會使用到 Steam 資料流,所以需要一個資料流轉成二進制檔案的 function ReadToEnd
public static byte[] ReadToEnd(System.IO.Stream stream)
    {
        long originalPosition = 0;

        if (stream.CanSeek)
        {
            originalPosition = stream.Position;
            stream.Position = 0;
        }

        try
        {
            byte[] readBuffer = new byte[4096];

            int totalBytesRead = 0;
            int bytesRead;

            while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
            {
                totalBytesRead += bytesRead;

                if (totalBytesRead == readBuffer.Length)
                {
                    int nextByte = stream.ReadByte();
                    if (nextByte != -1)
                    {
                        byte[] temp = new byte[readBuffer.Length * 2];
                        Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                        Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                        readBuffer = temp;
                        totalBytesRead++;
                    }
                }
            }

            byte[] buffer = readBuffer;
            if (readBuffer.Length != totalBytesRead)
            {
                buffer = new byte[totalBytesRead];
                Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
            }
            return buffer;
        }
        finally
        {
            if (stream.CanSeek)
            {
                stream.Position = originalPosition;
            }
        }
    }

4.然後把原本 ProcessRequest 的範例程式碼刪除,改成寫輸出PDF格式相關程式碼。
且注意報表位置 uri 為你自己的報表位置且如果有參數傳遞的寫法
public void ProcessRequest(HttpContext context)
    {

        context.Response.Buffer = true;
        //處理不要暫存檔案
        context.Response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0);
        context.Response.Expires = 0;
        context.Response.CacheControl = "no-cache";
        context.Response.AppendHeader("Pragma", "No-Cache");
        context.Response.ContentType = "application/pdf";

        Uri uri = new Uri(@"http://localhost/ReportServer?/RPReport&TitleName=ReportName&rs:Format=PDF");//
        //利用Webclient來提供驗證服務
        WebClient webClient = new WebClient();
        //驗證帳號密碼
        System.Net.NetworkCredential myCr = new NetworkCredential("帳號", "密碼");//Windows驗證非SQL驗證
        webClient.Credentials = myCr;
        //利用資料流傳輸資料
        Stream oStream = webClient.OpenRead(uri);
        //轉成二進制檔案格式輸出
        context.Response.BinaryWrite(ReadToEnd(oStream));
        //檔案的基本描述
        context.Response.AddHeader("Content-Disposition","attachment;filename=test.pdf" );//欲儲存的檔名修改區
       
      
    }


5.然後執行,即可轉成預存的的PDF存檔。
reporting service uri 用法詳細可參閱

URL Access (SSRS)

5 則留言:

  1. Dear Sir
    請問一下,依您提供的範例,不知你怎麼執行*.ashx,我是用一個*.aspx放置一個button呼叫javascript再用非同步執行*.ashx但都不會出現另存新檔的畫面,所以想請教一下,要如何執行*.ashx

    謝謝

    回覆刪除
  2. 或許可以利用一個頁面(Default.aspx)放置一個超連結NavigateUrl連結至該泛型程式應該就可以了(test.ashx)就可以了...
    不知道有沒有幫到你,謝謝

    回覆刪除
  3. Dear
    我用方式一可以,但不知為何方式二,非同步一樣執行同一個*.ashx,竟然不行,真的想不透....

    //方式一
    location.href = "Handler1.ashx?type=1";

    //方式二
    /*
    var ajaxPath = "";
    $.post(ajaxPath + "Handler1.ashx", { type: "1" },
    function (data) {

    });

    回覆刪除
  4. Dear gary
    試試看post方法改用get方法呢?

    回覆刪除
  5. Dear sir.
    不好意思
    看到你的範例,不曉得為什麼會一直跳出「遠端伺服器傳回一個錯誤: (401) 未經授權。」
    但若用IE開啟的話卻又可以正常瀏覽,登入帳號、密碼都相同
    請問是登的入機器需要做什麼設定嗎?

    回覆刪除