如何获取用户编辑后ASP.NET表单HTML内容并通过iText7生成PDF
问题背景
我目前有一个可导出为PDF的基础ASPX页面,实现逻辑是重写Render()方法、借助iText7组件完成PDF创建,该方案可正常运行,但实际效果为页面初次渲染时就会直接展示PDF,相关示例代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.IO; using iText.Kernel.Pdf; using iText.Layout; using iText.Layout.Element; using iText.Html2pdf; namespace Dch_Take_In_Form { public partial class _Default : Page { protected void Page_Load(object sender, EventArgs e) { } protected override void Render(HtmlTextWriter writer) { MemoryStream mem = new MemoryStream(); StreamWriter twr = new StreamWriter(mem); HtmlTextWriter myWriter = new HtmlTextWriter(twr); base.Render(myWriter); myWriter.Flush(); myWriter.Dispose(); StreamReader strmRdr = new StreamReader(mem); strmRdr.BaseStream.Position = 0; string pageContent = strmRdr.ReadToEnd(); strmRdr.Dispose(); mem.Dispose(); writer.Write(pageContent); CreatePDFDocument(pageContent); } public void CreatePDFDocument(string strHtml) { string strFileName = HttpContext.Current.Server.MapPath("test.pdf"); HtmlConverter.ConvertToPdf(strHtml, File.Create(strFileName)); ShowPdf(strFileName); } public void ShowPdf(string strFileName) { Response.ClearContent(); Response.ClearHeaders(); Response.AddHeader("Content-Disposition", "inline;filename=" + strFileName); Response.ContentType = "application/pdf"; Response.WriteFile(strFileName); Response.Flush(); Response.Clear(); } } }
我的目标是搭建包含各类文本框、复选框等控件的Web表单,由终端用户自行填写内容,填写完成后点击按钮控件,再基于用户输入的内容生成PDF;生成的PDF将在服务端静默存储,对终端用户完全透明。请问是否有相关实现建议或代码片段,可以最高效地将当前Web表单的内容读取为HTML字符串?
iText7中用于创建PDF的方法需要传入代表HTML页面代码的字符串作为参数之一,因此我需要获取当前Web表单的实际内容,为该方法提供入参。
实现方案
你现有写法的核心问题是把PDF生成逻辑绑定到了全局Render生命周期里,不管页面是首次加载还是回发都会触发,还直接清空响应流输出PDF,自然没法实现用户填完表单再触发的效果。调整思路很简单:
- 删掉重写的
Render方法里调用CreatePDFDocument的逻辑,后续不需要全局拦截页面渲染流 - 移除原有
ShowPdf方法,不需要把PDF输出到响应流,避免干扰页面正常回发渲染 - 新增提交按钮,把PDF生成逻辑放到按钮的点击回发事件里,这个阶段所有表单控件已经加载完用户提交的输入值,直接渲染目标控件拿到HTML字符串即可
- 生成PDF时直接存到服务端指定目录,不要修改响应内容,用户提交完正常看到表单提交结果就行
可直接复用的代码
前端ASPX页面结构示例,把需要导出的表单控件统一放到容器里:
<form id="form1" runat="server"> <%-- 所有需要导出到PDF的输入控件都放在这个Panel容器内 --%> <asp:Panel ID="FormContainer" runat="server"> <div class="form-item"> <label>姓名:</label> <asp:TextBox ID="txtName" runat="server"></asp:TextBox> </div> <div class="form-item"> <label>是否确认信息属实:</label> <asp:CheckBox ID="cbConfirm" runat="server" /> </div> <%-- 其余文本框、复选框、下拉选择等控件按业务需求正常添加 --%> </asp:Panel> <asp:Button ID="btnSubmit" runat="server" Text="提交表单" OnClick="btnSubmit_Click" /> <asp:Literal ID="ltlMsg" runat="server"></asp:Literal> </form>
后端.cs代码调整:
using System; using System.IO; using System.Web.UI; using iText.Html2pdf; namespace Dch_Take_In_Form { public partial class _Default : Page { protected void Page_Load(object sender, EventArgs e) { // 原有页面初始化逻辑保留即可,给控件绑默认值记得加!IsPostBack判断 } protected void btnSubmit_Click(object sender, EventArgs e) { // 渲染表单容器拿到带用户输入值的HTML片段 string formContent = RenderControlToHtml(FormContainer); // 拼接完整HTML结构,加上PDF需要的样式,iText对常用CSS支持度很高 string fullHtml = $@" <!DOCTYPE html> <html> <head> <meta charset=""utf-8""> <style> body {{ font-family: "Microsoft Yahei", sans-serif; font-size: 14px; margin: 20px; }} .form-item {{ margin: 12px 0; }} h3 {{ text-align: center; }} </style> </head> <body> <h3>用户提交表单信息</h3> {formContent} </body> </html>"; // 生成PDF静默存储到服务端,文件名加时间戳和GUID避免多用户提交覆盖 string saveDir = Server.MapPath("~/FormPdfRecords/"); Directory.CreateDirectory(saveDir); // 自动创建目录,避免首次运行找不到路径 string savePath = Path.Combine(saveDir, $"form_{DateTime.Now:yyyyMMddHHmmss}_{Guid.NewGuid():N}.pdf"); HtmlConverter.ConvertToPdf(fullHtml, File.Create(savePath)); // 返回用户提交成功提示,不干扰正常页面渲染 ltlMsg.Text = "<p style='color:green'>表单提交成功,我们会尽快处理。</p>"; // 如需提交后清空表单,直接在这里重置各控件的Text/Checked等属性即可 } /// <summary> /// 把服务端控件渲染为HTML字符串 /// </summary> private string RenderControlToHtml(Control targetCtl) { using (StringWriter sw = new StringWriter()) using (HtmlTextWriter htw = new HtmlTextWriter(sw)) { targetCtl.RenderControl(htw); return sw.ToString(); } } // 重写该方法空实现,避免RenderControl触发“控件必须放在带runat=server的表单内”的异常 public override void VerifyRenderingInServerForm(Control control) { } } }
避坑提示
- 不要在PDF生成逻辑里调用
Response.Clear、Response.WriteFile这类修改响应流的方法,否则会导致页面回发后样式错乱、控件状态丢失 - 存储PDF的目录要给IIS应用程序池对应的运行账号分配写入权限,否则会抛出文件访问拒绝异常
- 如果需要导出整个页面的内容,只需要把
RenderControlToHtml的传入参数换成this.Page即可,不想导出的按钮、提示语等元素,可以给对应控件加专属CSS类,渲染完HTML后用正则过滤掉对应节点再传给iText - 如果PDF里没带上用户输入的值,优先检查Page_Load里的控件默认值绑定逻辑,没加
!IsPostBack判断会在回发时覆盖用户提交的内容
内容的提问来源于stack exchange,提问作者gregarican
相关产品推荐
相关产品推荐

