目前產品上一直使用的是微軟的SQL Server Reporting Services給使用者輸出報表。可是在WEB列印上一直是一個心病,因為要調用微軟的RSClient控制項,可是這個控制項在瀏覽器上存在相容性問題,在Google或者Firefox瀏覽器就肯定不能調出用戶端列印介面。諮詢過微軟,好像又說RSClient控制項最新版本能支援Google或者Firefox瀏覽器,不過目前SQL Server2012是未能支援。
於是最近看到FineReport,看到FineReport最推薦的是PDF列印,於是產生新的想法,微軟的Reporting Services是能夠匯出PDF功能,利用現有Report viewer控制項,增加PDF線上列印功能,然後將報表匯出PDF在伺服器上臨時目錄,將瀏覽器指向臨時產生PDF檔案,由瀏覽器線上開啟PDF檔案。從目前測試效果看,由於目前用戶端都支援開啟PDF控制項,效果非常好不錯。
從實現角度來看,主要解決兩個關鍵問題:1、Report viewer產生PDF檔案。2、定期將臨時目錄PDF檔案刪除。
這個都比較簡單,下面是伺服器代碼:
/// <summary> /// PDF線上列印(理論上支援IE瀏覽器和Google瀏覽器) /// </summary> /// <returns></returns> public ActionResult PrintPdf() { var serverReport = new ServerReport(); serverReport.ReportServerUrl = Engine.ReportServer; serverReport.ReportPath = Engine.ReportPath; serverReport.ReportServerCredentials = Engine.Credentials; if (Engine.Parames != null) { foreach (var item in Engine.Parames) { item.Visible = false; serverReport.SetParameters(item); } } //匯出列印PDF string mimeType; string encoding; string fileNameExtension; Warning[] warnings; string[] streams; var rptBytes = serverReport.Render("PDF", deviceInfo, out mimeType, out encoding, out fileNameExtension, out streams, out warnings); //將1天以前的臨時檔案全部刪除 DeleteTempPdfFile(); //將PDF檔案儲存到磁碟 var fileName = SavePdf2Disk(rptBytes); //在瀏覽器上輸出直接開啟 return Content(fileName); } /// <summary> /// 將PDF檔案儲存到磁碟 /// </summary> /// <param name="pdfContent">pdf內容</param> /// <returns></returns> public string SavePdf2Disk(byte[] pdfContent) { Random random = new Random(); //儲存到磁碟的臨時檔案,按日期加隨機數,可以每次調用的時候,刪除臨時檔案。 //檔案名稱為:6位日期+5位隨機數.pdf var dateString = DateTime.Now.ToString("yyyyMMdd"); var fileName = dateString + random.Next(99999, 1000000).ToStr() + ".pdf"; var fileTempPath = Server.MapPath("~/") + "//FileTemp//" + fileName; FileStream writeStream = new FileStream(fileTempPath, FileMode.Create, FileAccess.Write); MemoryStream readStream = new MemoryStream(pdfContent); int Length = 256; Byte[] buffer = new Byte[Length]; int bytesRead = readStream.Read(buffer, 0, Length); // write the required bytes while (bytesRead > 0) { writeStream.Write(buffer, 0, bytesRead); bytesRead = readStream.Read(buffer, 0, Length); } readStream.Close(); writeStream.Close(); return fileName; } /// <summary> /// 刪除臨時PDF檔案 /// </summary> /// <returns></returns> public void DeleteTempPdfFile() { var fileTempPath = Server.MapPath("~/") + "//FileTemp//"; string[] files = Directory.GetFiles(fileTempPath, "*.pdf"); int dateString = int.Parse(DateTime.Now.ToString("yyyyMMdd")); FileInfo fi; //檔案名稱為:6位日期,如果現在日期大於臨時檔案日期,則刪除 //將1天以前的臨時檔案全部刪除。 foreach (var file in files) { fi = new FileInfo(file); if(fi.Name.Length>8) { var fileDateName = fi.Name.Substring(0, 8); try { if (dateString > int.Parse(fileDateName)) { fi.Delete(); } } catch { } } } }
接下來是用戶端代碼,就幾句js指令碼。
“`
$(document).ready(function () { printPdf();}); function printPdf() { var aj = $.ajax({ url: "/Common/Report/PrintPdf", // 跳轉到 action data: { code: reportCode }, type: 'post', cache: false, dataType: 'text', success: function(data) { var pdfUrl = "/FileTemp/" + data; ShowReport(pdfUrl); }, error: function() { alert("異常。"); } }); } function ShowReport(url) { window.location.href = url; //$("#ReportFrame").attr("src", url); }<div id="dvmask" style="position: absolute; z-index: 999999; top: 0px; left: 0px; width: 100%; height: expression(document.body.clientHeight); min-height: 100%; background-color: #ddd; text-align: center; opacity: 0.8; filter: alpha(opacity=80);"> <div style="position: relative; top: 50px; filter: alpha(opacity=100); opacity: 1; color: Red; text-align: center;"> <img src='@Url.Content("~/Images/wait1.gif")' alt="請稍候" style="margin: auto auto 10px auto;" /> <br /> 正在載入PDF資料,請稍候... 如果沒有安裝PDF閱讀器,請安裝<a href="https://acrobat.adobe.com/us/en/products/pdf-reader.html">PDF閱讀器</a> </div></div>
下面是FineReport的效果,查看截圖。
本地效果差不多見下圖: