笔者是作信息系统开发的,经常要制作各种各样的报表,而且这些报表都要求能够导出成 PDF 或 Excel 文件。完成这项工作最简单的方法自然是使用 ReprotViewer 这个控件了---它提供报表预览功能,并且自带导出PDF 或 Excel 文件的功能。不过,有时我们不想在页面上放置 ReprotViewer 控件(多数都是因为页面空间有限或天杀的界面设计师没给你留放这个控件的地方),而是希望能让用户直接按一个按钮就可以预览并打印报表,请见下图:
好在 LocalReport 类提供了一个叫 Render() 的函数,可以用来实现PDF或Excel文件导出。通常的方法是:初始化LocalReport 类对象->使用 LocalReport 类的 Render() 函数导出PDF或Excel文件->将生成的文件保存在服务器硬盘的一个临时目录下->使用 javascript:window.open() 函数打开一个新的浏览器窗体->在这个新窗体中从服务器硬盘上读取刚刚生成的那个PDF或Excel文件->向 Page.Response 写入刚刚读取的文件内容->从服务器硬盘上删除这个已经没用了的PDF或Excel文件。
在这个颇为繁琐的流程里,生成一个临时的PDF或Excel文件这步让人很不舒服。首先,要在服务器的硬盘上创建一个临时文件夹。其次,要考虑多个用户同时导出临时文件时的文件重名问题。还有,文件用下载完了要删除,不然会污染环境。能不能把这步去掉呢?
答案是肯定的。我们可以先把生成的PDF文件内容保存到Session中,然后向 MyHandler.jxd 发送访问请求,在由 MyHandler.jxd 将 Session 中的文件内容发送到客户端。具体的流程是:初始化 LocalReport 类对象->使用 LocalReport 类的 Render() 函数导出PDF或Excel文件->将生成的文件内容保存到 Session 中->使用 javascript:window.open() 函数打开一个新的浏览器窗体访问 MyHandler.jxd ->MyHandler.jxd 接收到访问请求后将 Session 中的文件内容发送到客户端。具体的实现方法请看下面的源代码。
源代码
MyHandler.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace mylib.system.web
{
public class MyHandler : System.Web.IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
#region IHttpHandler 成员
public bool IsReusable
{
get { return false; }
}
public static string msg
{
get { return System.Web.HttpContext.Current.Session["mylib.system.web.MyHandler.msg"] as string; }
set { System.Web.HttpContext.Current.Session["mylib.system.web.MyHandler.msg"] = value; }
}
public static string content_type
{
get { return System.Web.HttpContext.Current.Session["mylib.system.web.MyHandler.content_type"] as string; }
set { System.Web.HttpContext.Current.Session["mylib.system.web.MyHandler.content_type"] = value; }
}
public static byte[] content
{
get { return System.Web.HttpContext.Current.Session["mylib.system.web.MyHandler.content"] as byte[]; }
set { System.Web.HttpContext.Current.Session["mylib.system.web.MyHandler.content"] = value; }
}
public void ProcessRequest(System.Web.HttpContext context)
{
context.Response.ContentType = content_type;
context.Response.OutputStream.Write(content, 0, content.Length);
content = null; // 释放内存
}
#endregion
}
}
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>无标题页</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="open_pdf_button" runat="server" OnClick="open_pdf_button_Click" Text="报表打印"
Width="106px" />
</div>
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void open_pdf_button_Click(object sender, EventArgs e)
{
// 生成报表
Microsoft.Reporting.WebForms.LocalReport rpt
= new Microsoft.Reporting.WebForms.LocalReport();
rpt.ReportPath = Server.MapPath("~/Report.rdlc");
// 由报表生成 PDF 文件,并保存在内存中
Microsoft.Reporting.WebForms.Warning[] Warnings;
string[] strStreamIds;
string strMimeType;
string strEncoding;
string strFileNameExtension;
byte[] bytes = rpt.Render("PDF", null, out strMimeType, out strEncoding, out strFileNameExtension,
out strStreamIds, out Warnings);
// 通过 Session 将动态生成的PDF文件的内容与 MyHandler 共享
mylib.system.web.MyHandler.content_type = "application/pdf";
mylib.system.web.MyHandler.content = bytes;
// 打开一个新的窗体,向 MyHandler.jxd 发出访问请求
if (!Page.ClientScript.IsStartupScriptRegistered(Page.GetType(), "OpenFile"))
{
Page.ClientScript.RegisterStartupScript(Page.GetType(), "OpenFile",
"window.open('MyHandler.jxd');", true);
}
}
}
注意上面的代码中用于执行javascript语句的代码是
Page.ClientScript.RegisterStartupScript(Page.GetType(), "OpenFile",
"window.open('MyHandler.jxd');", true);
而不是用Response.Write()。这是因为使用Response.Write()函数向客户端输出信息有可能破坏HTML代码的结构!我的一个同事有一次发现她的一个Web应用程序的页面格式在设计时看起来好好的,一旦运行起来就会变得很难看。她百思不得其解,直想杀死比尔。后来发现原来是她的代码使用Response.Write()向页面输出了一些代码,只要把这些对Response.Write()调用全部换成Page.ClientScript.RegisterStartupScript() 就没问题了。
下载本篇全部源代码
本篇到此结束,下篇将介绍另一个应用HTTP 处理程序的实例:在Web程序中显示、打印条码的解决方案。
本系列共6篇文章
实战 HTTP 处理程序(HTTP Handler) (6)——条码随意打
实战 HTTP 处理程序(HTTP Handler) (5)——不用临时文件,直接打开动态生成的文件 <- you are here.
实战 HTTP 处理程序(HTTP Handler) (4)——与Web程序共享Session
实战 HTTP 处理程序(HTTP Handler) (3)——动态生成图片
实战 HTTP 处理程序(HTTP Handler) (2)——向HTTP 处理程序传递参数
实战 HTTP 处理程序(HTTP Handler) (1)——创建一个最简单的 HTTP Handler