我有一个简单的 ASP.Net Core MVC 应用程序,它是一种基本的输入形式,上面有一个 HTML 画布(用于签名)。填写表格后,我需要将其转换为 PDF 并将其附加到电子邮件中。我发现SelectPDF有一个支持 .Net Core 的免费社区版,我想试试看。
我有我的应用程序在一个地方,我可以提交表单并在单独的视图中查看完成的表单(完整的图像代表用户在画布中输入的内容)。电子邮件发送得很好,但我终生无法从渲染视图中生成 PDF。
在我花了几天时间尝试解决之前我不知道的是,这个带有 SelectPdf 的解决方案在新会话中对 URL 执行 GET - 这意味着我需要提供大量请求,因为我的表单有大约 20 个字段,包括超过请求大小限制的转换图像。
我试图在不使用数据库或服务的情况下做到这一点,但图像证明这是我所期望的更具挑战性的工作。
我已经在 SO 和其他站点上看到并尝试了许多建议的解决方案。它们要么已经使用了几年(在某些情况下是十年或更长时间)并且已经过时,要么试图使问题变得比使用其他几个工具或扩展(其中大部分是付费的或过时的)要复杂得多。
有没有办法让我:
- 以某种方式修剪图像 URL?或者
- 允许 GET 请求接受这么长的请求?
- 以某种方式将填充了值的渲染视图转换为 HTML 字符串并发送它而不是 URL?
- 使用 Tempdata 在“显示”视图中重建我的模型以绕过请求长度限制?
任何关于如何完成我想做的事情的建议或建议都会很棒。
编辑:更多代码和到目前为止我尝试过的内容(扩展)
模型:
namespace Website.Models
{
//[Serializable]
public class ComputerRepairModel
{
[Required(AllowEmptyStrings = false)]
[Display(Name="Customer Name")]
public string CustomerName { get; set; }
[Display(Name = "Email")]
public string CustomerEmail { get; set; }
[Display(Name = "Home")]
public string ContactHomeNumber { get; set; }
[Display(Name = "Work")]
public string ContactWorkNumber { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Cell")]
public string ContactCellNumber { get; set; }
[Display(Name = "Signed")]
public string Signature { get; set; }
....
}
控制器:
namespace Website.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult RepairAgreement()
{
ComputerRepairModel model = new ComputerRepairModel();
return View(model);
}
[HttpPost]
public IActionResult RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
return View(Model);
}
[HttpGet]
public IActionResult DisplayRepairAgreement()
{
//ComputerRepairModel model = (ComputerRepairModel)TempData["model"];
return View();
}
[HttpPost]
public IActionResult SubmitRepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = null;
return View("RepairAgreement", Model);
}
//TempData["model"] = Model;
return RedirectToAction("DisplayRepairAgreement");
}
看法:
@model ComputerRepairModel
@section Scripts{
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script>
$(function () {
var canvas = document.querySelector('#signatureCanvas');
var pad = new SignaturePad(canvas);
});
</script>
<script>
$("#submit").click(function () {
//alert("button"); // Remove this line if it worked
var dataURL = document.getElementById('signatureCanvas').toDataURL();
document.getElementById('signature').value = dataURL;
$("#submitbutton").hide();
});
</script>
}
<head>
</head>
<body>
<h2 style="margin-top:20px;">Computer Repair Form</h2>
<hr />
<form method="post" asp-action="SubmitRepairAgreement">
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="CustomerName"></label>
<input type="text" asp-for="CustomerName" class="form-control" />
<span asp-validation-for="CustomerName" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="CustomerEmail"></label>
<input type="text" asp-for="CustomerEmail" class="form-control" placeholder="example@domain.com" />
<span asp-validation-for="CustomerEmail" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Contact Number(s)</b></label>
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="ContactHomeNumber"></label>
@*<input type="text" asp-for="ContactHomeNumber" class="phone form-control" maxlength="14" />*@
<input id="homePhone" class="form-control" type="text" asp-for="ContactHomeNumber" />
<span asp-validation-for="ContactHomeNumber" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="ContactWorkNumber"></label>
<input id="workPhone" class="form-control" type="text" asp-for="ContactWorkNumber" />
<span asp-validation-for="ContactWorkNumber" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="ContactCellNumber"></label>
<input id="cellPhone" class="form-control" type="text" asp-for="ContactCellNumber" />
<span asp-validation-for="ContactCellNumber" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Billing Address</b></label>
<div class="form-row">
<div class="form-group col-sm-5">
<label asp-for="BillingStreetAddress"></label>
<input class="form-control" type="text" asp-for="BillingStreetAddress" />
<span asp-validation-for="BillingStreetAddress" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingCity"></label>
<input class="form-control" type="text" asp-for="BillingCity" />
<span asp-validation-for="BillingCity" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingState"></label>
<input class="form-control" type="text" asp-for="BillingState" />
<span asp-validation-for="BillingState" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingZip"></label>
<input class="form-control" type="text" asp-for="BillingZip" />
<span asp-validation-for="BillingZip" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Computer Access</b></label>
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="CustomerComputerUsername"></label>
<input class="form-control" type="text" asp-for="CustomerComputerUsername" />
<span asp-validation-for="CustomerComputerUsername" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="CustomerComputerPassword"></label>
<input class="form-control" type="text" asp-for="CustomerComputerPassword" />
<span asp-validation-for="CustomerComputerPassword" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="DescriptionOfProblem"></label>
<textarea class="form-control" asp-for="DescriptionOfProblem"></textarea>
<span asp-validation-for="DescriptionOfProblem" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="ItemsReceived"></label>
<textarea class="form-control" asp-for="ItemsReceived"></textarea>
<span asp-validation-for="ItemsReceived" class="text-danger"></span>
</div>
</div>
</div>
<hr />
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="Comments"></label>
<textarea class="form-control" asp-for="Comments"></textarea>
<span asp-validation-for="Comments" class="text-danger"></span>
</div>
</div>
</div>
<div>
I hereby agree to the above terms and authorize AMTI to perform services/repairs as stated in the service order.<br />
I also agree to the terms and conditions within this Agreement.
</div>
<div class="form-group" style="margin-top:20px;">
<div class="form-row justify-content-between">
<div class="col-sm-6">
<label asp-for="Signature"></label>
@if (String.IsNullOrEmpty(Model.Signature))
{
<input type="hidden" id="signature" asp-for="Signature" />
<canvas width="500" height="100" id="signatureCanvas" style="border:1px solid black"></canvas>
}
else
{
<img src="@Url.Content(Model.Signature)" alt="Image" />
}
</div>
<div class="form-group col-sm-3">
<label asp-for="DateSigned"></label>
<input class="form-control" type="date" asp-for="DateSigned"/>
</div>
</div>
</div>
<div>
<hr />
<center><b>For Office Use Only</b></center>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-4">
<label asp-for="ComputerMfg"></label>
<input class="form-control" readonly asp-for="ComputerMfg" />
</div>
<div class="form-group col-sm-4">
<label asp-for="ComputerModel"></label>
<input class="form-control" readonly asp-for="ComputerModel" />
</div>
<div class="form-group col-sm-4">
<label asp-for="ComputerOS"></label>
<input class="form-control" readonly asp-for="ComputerOS" />
</div>
</div>
</div>
</div>
<div id="submitbutton">
<input id="submit" class="form-control button" style="background-color: #4CAF50; color:white;" type="submit"/>
</div>
</form>
</body>
上面显示的基本上是我的模型、控制器和视图的样子。
我的模型和控制器中的注释代码代表了我最近从这个答案中解决问题的尝试。显然,如果我想尝试让这种方法发挥作用,我还有一些工作要做,因为尽管将我的模型标记为可序列化,但我收到以下错误。
我尝试了这个,因为如果我只是做一个正常RedirectToAction("DisplayRepairAgreement", Model);
的请求会太长(因为我通过 Javascript 将 HTML 画布转换为 URL 字符串),如图所示。
我尝试的另一件事是使用相同的视图并让 POST 操作成为用于发送到 PDF 转换的操作(这就是为什么我在底部的签名输入附近有 if 条件)但这只会抓住GET
当我将 URL 传递给该方法并将表单保存为 PDF 时,但没有填写任何值。
以下是我在最近一次尝试之前在控制器中执行的更多操作(如上所示):
[HttpPost]
public IActionResult RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
string url = Url.Action(nameof(DisplayRepairAgreement),
new { Model.CustomerName, Model.CustomerEmail, Model.ContactHomeNumber, Model.ContactWorkNumber, Model.ContactCellNumber,
Model.BillingStreetAddress, Model.BillingCity, Model.BillingState, Model.BillingZip, Model.CustomerComputerUsername, Model.CustomerComputerPassword, Model.DescriptionOfProblem,
Model.ItemsReceived, Model.Comments, Model.Signature, Model.DateSigned});
// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();
// create a new pdf document converting an url
PdfDocument doc = converter.ConvertUrl(url);
// save pdf document
doc.Save("Sample.pdf");
// close pdf document
doc.Close();
return View(Model);
}
在我绝望中,我还尝试直接在我的模型中对我的视图的 HTML 进行硬编码,因为 SelectPDF 对象的方法之一可以接收 HTML 字符串而不是 URL。我填写了表格并被带到了显示视图,在那里我使用检查器抓取了整个 HTML 块并将其粘贴进去。它几乎可以工作了。本质上,在我的操作中,我只需调用以下方法,并且传入的 Html 存储在模型中,如本段前面所述。
public PdfDocument CreatePdfFromHTML(string Html)
{
HtmlToPdf converter = new HtmlToPdf();
PdfDocument pdfDoc = converter.ConvertHtmlString(Html);
return pdfDoc;
}
这是表单在浏览器中的外观,以及我希望 PDF 的外观
以下是我尝试使用 stringbuilder 方法并根据 Chrome 中的检查器编写自己的 HTML 字符串时的外观。