在ASP.NET MVC 無需Web Form和Report Viewer 預覽SSRS報表解決方案
2013-06-07 20:46 靈感之源 閱讀(9925) 評論(12) 收藏 舉報環境
ASP.NET MVC 4.0 + SQL Server Reporting Services
需求
在保存報表為文件(如PDF)之前,可以預覽報表(支持圖片)。
分析
網絡上的解決方案,都是告訴你用最原始的辦法:結合ASP.NET Web Form+Report Viewer控件。因為SQL Server Reporting Services (SSRS) 只有Web Form的Report Viewer控件,沒對ASP.NET MVC進行特別支持。
我們不能直接在ASP.NET MVC用Report Viewer是因為Report Viewer依賴View State,而View State正是MVC要解決的問題。。。
因為不滿意這種整合Web Form的解決方案,今晚思考了一下,既然SSRS支持渲染結果為MHTML(MIME HTML),那么,如果我們解決瀏覽器不顯示MHTML內容(需要另存再本地打開),就可以實現預覽的辦法:先渲染成MHTML,再解釋MIME,再把圖片附件轉成data嵌入。
步驟
1. 設計頁面
因為預覽結果是完整的HTML,我在報表頁面嵌入了一個iframe。。。。。。src指向一個Web API,該Web API會返回渲染后的html內容(包括圖片)。
2. 下載文件
添加一個Web API 函數,用以渲染MHTML并返回text/html結果。為了簡化操作,我把報表參數拼接起來,鍵值用“|”分隔,參數用“`”分隔。實際使用請根據自己的情況修改。。。
public HttpResponseMessage DownloadReportContent(string ReportFileName, string Parameters)
{
var parameters = new Dictionary<string, string>();
Parameters.Split('`').ForEach(p =>
{
var parts = p.Split('|');
parameters.Add(parts[0], parts[1]);
});
byte[] reportContent;
string fileExtension, mimeType;
ReportGenerator.GenerateReport("ReportServer", "ReportServerExecutionURL", "ReportServerUserName", "ReportServerPassword", "ReportServerDomain", ReportFileName, ExportFormat.MHTML, parameters, string.Empty, out reportContent, out fileExtension, out mimeType);
reportContent = ReportConvertor.Convert(reportContent);
var resultStream = new System.IO.MemoryStream(reportContent);
resultStream.Position = 0;
var response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(resultStream);
return response;
}
3. 渲染報表為MHTML
下面的方法可以用作渲染其它格式,如最常見的PDF
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
//using YOUR_IMPORTED_WEB_REFERENCE_FOR_SSRS_EXECUTION_SERVICE_HERE
namespace Org.SSRSPreview
{
/// <summary>
/// Export Formats
/// </summary>
public enum ExportFormat
{
/// <summary>XML</summary>
XML,
/// <summary>Comma Delimitted File
CSV,
/// <summary>TIFF image</summary>
Image,
/// <summary>PDF</summary>
PDF,
/// <summary>HTML (Web Archive)</summary>
MHTML,
/// <summary>HTML 4.0</summary>
HTML4,
/// <summary>HTML 3.2</summary>
HTML32,
/// <summary>Excel</summary>
Excel,
/// <summary>Word</summary>
Word
}
public class ReportGenerator
{
/// <summary>
/// Gets the string export format of the specified enum.
/// </summary>
/// <param name="Format">export format enum</param>
/// <returns>enum equivalent string export format</returns>
private string GetExportFormatString(ExportFormat Format)
{
switch (Format)
{
case ExportFormat.XML: return "XML";
case ExportFormat.CSV: return "CSV";
case ExportFormat.Image: return "IMAGE";
case ExportFormat.PDF: return "PDF";
case ExportFormat.MHTML: return "MHTML";
case ExportFormat.HTML4: return "HTML4.0";
case ExportFormat.HTML32: return "HTML3.2";
case ExportFormat.Excel: return "EXCEL";
case ExportFormat.Word: return "WORD";
default:
return "PDF";
}
}
/// <summary>
/// generate a report
/// </summary>
/// <param name="ReportServer">report server</param>
/// <param name="ReportServerExecutionURL">rendering execution url of report server</param>
/// <param name="ReportServerUserName">user name to access report server</param>
/// <param name="ReportServerPassword">password of the user name</param>
/// <param name="ReportServerDomain">domain of the report server (for active directory)</param>
/// <param name="ReportServerSubDir">folder of the report in the report server</param>
/// <param name="FileFormat">output of the report</param>
/// <param name="Parameters">parameters for the report</param>
/// <param name="ReportContent">rendered report content</param>
/// <param name="FileExtension">file extension of the report</param>
/// <param name="MimeType">mime type of the generated file</param>
public void GenerateReport(string ReportServer,
string ReportServerExecutionURL,
string ReportServerUserName,
string ReportServerPassword,
string ReportServerDomain,
string ReportServerSubDir,
string ReportFileName,
ExportFormat FileFormat,
Dictionary<string, string> Parameters,
string DeviceInfo,
out byte[] ReportContent,
out string FileExtension,
out string MimeType)
{
using (var reportExecutionService = new ReportExecutionService.ReportExecutionServiceSoapClient("ReportExecutionServiceSoap", ReportServerExecutionURL))
{
reportExecutionService.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
reportExecutionService.ClientCredentials.UserName.UserName = ReportServerUserName;
reportExecutionService.ClientCredentials.UserName.Password = ReportServerPassword;
reportExecutionService.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(ReportServerUserName, ReportServerPassword, ReportServerDomain);
var parameters = Parameters.Select(p => new ParameterValue { Name = p.Key, Value = p.Value }).ToList();
// Init Report to execute
ServerInfoHeader serverInfoHeader;
ExecutionInfo executionInfo;
ExecutionHeader executionHeader = reportExecutionService.LoadReport(null, ReportFileName, null, out serverInfoHeader, out executionInfo);
// Attach Report Parameters
reportExecutionService.SetExecutionParameters(executionHeader, null, parameters.ToArray(), null, out executionInfo);
// Render
string encoding;
Warning[] warnings;
string[] streamIds;
reportExecutionService.Render(executionHeader, null, GetExportFormatString(FileFormat), DeviceInfo, out ReportContent, out FileExtension, out MimeType, out encoding, out warnings, out streamIds);
}
}
}
}
4. 解釋MIME,轉換圖片附件為data嵌入式
這是整個解決方案的核心思想。SSRS會把修改圖片的標識,譬如<img src="cid:SOME_ID_HERE">,我們要做的就是取得圖片附件的原始base64內容,構造成如下的格式:
data:image/png;base64,[ENCODED_DATA_HERE]
MIME解釋用到了OpenPop.NET (通過Pop3協議收取郵件),這是我10年前參與開發的第一個開源項目,不過幾年前把項目移交給別的開發人員,我當年寫的代碼部分都蕩然無存了。。。在這里吐槽一下接手的開發人員,你太不給偶面子了。。。 :-)
為了優化性能,我改動了一下MessagePart.cs文件。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OpenPop.Mime;
namespace Org.SSRSPreview
{
public class ReportConvertor
{
public static byte[] Convert(byte[] Content)
{
var message = new Message(Content);
var body = message.FindFirstHtmlVersion();
var bodyText = body.BodyEncoding.GetString(body.Body, 0, body.Body.Length);
message.FindAllAttachments().ForEach(a =>
{
if (!a.IsText)
{
var key = "Content-Location";
var url = a.Headers.UnknownHeaders[key];
if (string.IsNullOrEmpty(url) && a.ContentDisposition != null)
url = a.ContentDisposition.Inline ? "cid:" + a.FileName : a.FileName;
var attachment = a.Body;
var embedImage = new StringBuilder("data:");
embedImage.Append(a.ContentType.MediaType + ";");
embedImage.Append(a.ContentTransferEncoding.ToString().ToLowerInvariant() + ",");
embedImage.Append(a.BodyEncoding.GetString(a.RawBody));
bodyText = bodyText.Replace(url, embedImage.ToString());
}
});
return body.BodyEncoding.GetBytes(bodyText);
}
}
}
限制
沒有Report Viewer那些控制,譬如縮放,分頁等。。。。我不需要啊。。。
代碼
點擊這里下載。代碼不是完整的解決方案,只包括關鍵的代碼,如果你打開運行,只會解釋一個例子MHTML,因為相關的函數和類都在這里貼了。
總結
至此,大功告成。SQL Server團隊對ASP.NET MVC不重視啊,這么多年還不考慮一下支持ASP.NET MVC。
浙公網安備 33010602011771號