动态页面转静态页面+HTML转换PDF+导出PDF
这几天客户有一个需求:学生的成绩提供打印和导出PDF格式文件两种方法。由于之前没有接触过生成PDF文件,所以在这个上面话了一点时间。下面是这两天的研究成果:
生成PDF文件在网上有很多资源。大部分是用到ITextSharp。我就省事直接从别人的例子里面拿过来了。
1 private void CreatPdf() 2 { 3 Document doc = new Document(PageSize.A4, 9, 18, 36, 36);//左右上下 4 MemoryStream ms = new MemoryStream(); 5 try 6 { 7 PdfWriter writer = PdfWriter.GetInstance(doc, ms); 8 writer.CloseStream = false; 9 doc.Open(); 10 url =Request.Url.AbsoluteUri; 11 Thread thread = new Thread(new ThreadStart(setBitmap)); 12 thread.SetApartmentState(ApartmentState.STA); 13 thread.Start(); 14 while (thread.IsAlive) 15 Thread.Sleep(100); 16 bitmap.Save(Server.MapPath("t.bmp")); 17 18 iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(bitmap, System.Drawing.Imaging.ImageFormat.Bmp); 19 img.ScalePercent(75);//560 630 20 doc.Add(img); 21 } 22 catch (Exception err) 23 { 24 throw new Exception(err.Message); 25 } 26 finally 27 { 28 doc.Close(); 29 using (FileStream fs = new FileStream(Server.MapPath("out.pdf"), FileMode.Create)) 30 { 31 ms.Position = 0; 32 byte[] bit = new byte[ms.Length]; 33 ms.Read(bit, 0, (int)ms.Length); 34 fs.Write(bit, 0, bit.Length); 35 } 36 System.IO.File.Delete(ViewState["pdf"].ToString()); 37 ViewPdf(ms); 38 } 39 } 40 41 private void ViewPdf(Stream fs) 42 { 43 Response.Clear(); 44 //中文名的话 45 //Response.AppendHeader("Content-Disposition", "attachment;filename=" + 46 // HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8) + ";charset=GB2312"); 47 Response.AddHeader("Content-Disposition", "attachment;FileName=out.pdf"); 48 Response.AddHeader("Content-Length", fs.Length.ToString()); 49 Response.ContentType = "application/pdf"; 50 long fileLength = fs.Length; 51 int size = 10240;//10K一--分块下载,10K为1块 52 byte[] readData = new byte[size]; 53 if (size > fileLength) 54 size = Convert.ToInt32(fileLength); 55 long fPos = 0; 56 bool isEnd = false; 57 while (!isEnd) 58 { 59 if ((fPos + size) >= fileLength) 60 { 61 size = Convert.ToInt32(fileLength - fPos); 62 isEnd = true; 63 } 64 readData = new byte[size]; 65 fs.Position = fPos; 66 fs.Read(readData, 0, size); 67 Response.BinaryWrite(readData); 68 Response.OutputStream.Flush(); 69 fPos += size; 70 } 71 fs.Close(); 72 Response.OutputStream.Close(); 73 Response.End();//非常重要,没有这句的话,页面的HTML代码将会保存到文件中 74 Response.Close(); 75 }
View Code
最主要的问题是,怎么样把本页面转换成PDF?ITextSharp没有直接把html画到PDF文件的方法。
首先,有一个想法,那就是把页面转换成图片。再把这个图片加到PDF文件就方便了。
那么怎么把本页面转换成图片呢?在System.Windows.Forms.WebBrowser类下有一个DrawToBitmap方法,好了,这就简单了。我们创建一个WebBrowser实例,让它把本页面加载进去再画图片就可以了。
1 public void setBitmap() 2 { 3 using (WebBrowser wb = new WebBrowser()) 4 { 5 wb.Width = w; 6 wb.Height = h; 7 wb.ScrollBarsEnabled = false; 8 wb.Navigate(ViewState["pdf"].ToString()); 9 //确保页面被解析完全 10 while (wb.ReadyState != WebBrowserReadyState.Complete) 11 { 12 System.Windows.Forms.Application.DoEvents(); 13 } 14 bitmap = new System.Drawing.Bitmap(w, h); 15 wb.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, w, h)); 16 wb.Dispose(); 17 } 18 }
View Code
这个想法是可以的,在一些不需要登录信息的页面是可用的,可是我想打印的是学生成绩。必须需要登录才拿到成绩信息打印出来。当新建一个WebBrowser再指向成绩页面的时候就没有了登录信息。成为了匿名访问。打印出来的是系统的登录页面。
解决这个问题的时候我想到了单点登录的方法,但是想想为了一个导出功能用上了单点登录有点大材小用了。就在苦苦思考的时候突然想了起来,既然上面的导出方法对于导出静态页面是有效的,为什么不在点击打印的时候把本页面转换成静态html页面保存在服务器目录上提供导出,再导出之后再删掉呢?
现在问题又变了,如何导出静态页面呢?这个简单。
1 protected void Button1_Click(object sender, EventArgs e) 2 { 3 //把页面保存成静态页面再导出,这样就不会因为未登录而打印到登陆页面。 4 //第一步: 5 string paf = Guid.NewGuid ().ToString();//获取唯一表示。防止多用户并发时导出别人页面。 6 ViewState["pdf"] = Server.MapPath("~") + paf + "pdf.html";//静态页面的保存路径。为了图片能够显示,Server.MapPath("~")你得改成你相应的文件夹下。 7 ToHTML("~/PDFForm.aspx?type=pdf", ViewState["pdf"].ToString());//保存静态页面。 8 //第二部:导出PDF文件 9 CreatPdf(); 10 } 11 private void ToHTML(string url, string savefile) 12 { 13 StreamWriter sw = new StreamWriter(savefile, false, System.Text.Encoding.GetEncoding("gb2312")); 14 Server.Execute(url, sw, true); 15 16 sw.Flush(); 17 sw.Close(); 18 }
View Code
就在以为大事告成的时候问题出现了,因为是在点击导出按钮的时候生成静态页面,又要访问本页面一次,这就造成了死循环。这次的解决方法是:在生成静态页面的时候带上参数,在Page_Load事件里面把按钮的时间去掉。这就可以了。
1 private System.Drawing.Bitmap bitmap; 2 private string url; 3 private int w = 760, h = 900; 4 protected void Page_Load(object sender, EventArgs e) 5 { 6 if (Request["type"] != null && Request["type"] == "pdf") 7 { 8 Button1.Visible = false; 9 Button1.Click -= Button1_Click;//如果没有写,会成为死循环! 10 } 11 }
View Code