目前产品上一直使用的是微软的SQL Server Reporting Services给用户输出报表。可是在WEB打印上一直是一个心病,因为要调用微软的RSClient控件,可是这个控件在浏览器上存在兼容性问题,在谷歌或者火狐浏览器就肯定不能调出客户端打印界面。咨询过微软,好像又说RSClient控件最新版本能支持谷歌或者火狐浏览器,不过目前SQL Server2012是未能支持。
于是最近看到FineReport,看到FineReport最推荐的是PDF打印,于是产生新的想法,微软的Reporting Services是能够导出PDF功能,利用现有Report viewer控件,增加PDF在线打印功能,然后将报表导出PDF在服务器上临时目录,将浏览器指向临时生成PDF文件,由浏览器在线打开PDF文件。从目前测试效果看,由于目前客户端都支持打开PDF控件,效果非常好不错。
从实现角度来看,主要解决两个关键问题:
1、Report viewer生成PDF文件。
2、定期将临时目录PDF文件删除。
这个都比较简单,下面是服务器代码:
/// <summary>
/// PDF在线打印(理论上支持IE浏览器和谷歌浏览器)
/// </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的效果,查看截图。
本地效果差不多见下图: