在ASP.NET MVC 无需Web Form和Report Viewer 预览SSRS报表解决方案
2013-06-07 20:46 灵感之源 阅读(9909) 评论(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。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2004-06-07 《软件工艺》-初评