У меня есть простое приложение ASP.Net Core MVC, которое представляет собой базовую форму ввода с HTML-канвой на нем (для подписи).Когда форма заполнена, мне нужно преобразовать ее в PDF и прикрепить к электронному письму.Я нашел SelectPDF , который имеет бесплатную версию для сообщества, поддерживающую .Net Core, и решил, что мне это удастся.
Я получил свое заявление в месте, где я могу отправитьзаполнить форму и просмотреть заполненную форму в отдельном виде (в комплекте с изображением для представления того, что пользователь ввел на холст).Электронные письма отправляются просто отлично, но я не могу на всю жизнь сгенерировать PDF из своего отрисованного представления.
Чего я не знал, пока не потратил несколько дней на попытки решить, было ли это решение сSelectPdf выполняет GET для URL-адреса в новом сеансе. Это означает, что мне нужно было бы выполнить массивный запрос, поскольку моя форма содержит ~ 20 полей, включая преобразованное изображение, которое превышает лимит размера запроса.
I 'Я пытаюсь сделать это без использования базы данных или службы, но изображение доказывает, что это гораздо более сложная задача, чем я ожидал.
Я видел и попробовал многие из предложенных решений на 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>
Показанный выше, по сути, мой внешний вид модели, контроллера и вида.
Закомментированный код в моей модели и контроллере представляет мою последнюю попытку решения проблемы из этого ответа .Очевидно, у меня еще есть работа, если я хочу попробовать и заставить этот метод работать, так как, несмотря на пометку моей модели как Serializable, я получаю следующую ошибку.
Я попытался это сделать, потому что, если бы я просто сделал обычный RedirectToAction("DisplayRepairAgreement", Model);
, запрос был бы слишком длинным (так как я преобразую холст HTML в строку URL через Javascript), как показано,
Еще одна вещь, которую я попробовал, состояла в том, чтобы просто использовать то же представление и использовать действие 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 и написал свою собственную строку HTMLна базе инспектора в Chrome.