angular - 保存表单数据后缺少文件文档附件
问题描述
我有一个包含文档上传功能的简单表单。当我提交数据时,文件上传数据保存成功,但有时上传的文件没有保存在服务器路径上,并且文档列在数据库中更新为 NULL。此问题并非每次都会发生,但在极少数情况下会发生。我检查了日志文件,但没有发现任何异常,也没有例外,提交数据后它只是说数据提交成功但上传的文档丢失。
'home-component.ts'
Submit() {
if (this.requestForm.invalid || !this.IsDocumentUploaded) {
for (const key of Object.keys(this.requestForm.controls)) {
if (this.requestForm.controls[key].invalid) {
const invalidControl = this.el.nativeElement.querySelector('[formcontrolname="' + key + '"]');
invalidControl.focus();
break;
}
}
return;
}
this.ShowPageOneDiv = false;
this.ShowSpinner = true;
this.apiService.Submit(this.requestForm.value).subscribe(
res => {
const recaptcha = res;
const filesToUpload = [];
filesToUpload.push(this.DocumentFile);
if (this.IsCoverLetterUploaded) {
filesToUpload.push(this.CoverLetterFile);
}
this.apiService.UploadFile(recaptcha, filesToUpload).subscribe(
() => {
'Send notification to user'
this.apiService.Notify(recaptcha).subscribe(
docId => {
this.RegistryNumber = docId;
this.ShowConfirmationMessage = true;
this.ShowSpinner = false;
},
err => {
this.ShowErrorMessage = true;
this.ShowSpinner = false;
console.log(err);
}
)
},
() => {
console.log("Send notification to user anyway, but indicate that file upload failed");
this.apiService.Notify(recaptcha).subscribe(
registryNumber => {
this.RegistryNumber = registryNumber;
this.ShowFailedUploadMessage = true;
this.ShowSpinner = false;
},
err => {
this.ShowErrorMessage = true;
this.ShowSpinner = false;
console.log(err);
}
)
}
)
},
error => {
this.ShowErrorMessage = true;
this.ShowSpinner = false;
console.log(error);
});
}
'应用服务.ts'
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ProductionUpload } from './ProductionUpload';
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(private _http: HttpClient) { }
Submit(productionUpload: ProductionUpload) {
const apiUrl = window.location.href + 'ProductionUpload/Add';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
}),
responseType: 'text' as 'json'
};
productionUpload.ReplacementFiles = +productionUpload.ReplacementFiles;
productionUpload.IsDeleted = false;
productionUpload.IsProcessed = false;
return this._http.post<string>(apiUrl, productionUpload, httpOptions);
}
UploadFile(recaptcha, fileToUpload: File[]) {
const apiUrl = window.location.href + 'ProductionUpload/Upload';
const formData: FormData = new FormData();
console.log("add the files");
if (fileToUpload && fileToUpload.length) {
fileToUpload.forEach(file => formData.append('files', file));
}
formData.append('data', recaptcha)
return this._http.post(apiUrl, formData);
}
Notify(recaptcha) {
const apiUrl = window.location.href + 'ProductionUpload/Notify/';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
}),
responseType: 'text' as 'json'
};
return this._http.post<string>(apiUrl, JSON.stringify(recaptcha), httpOptions);
}
}
'我的控制器.cs'
[HttpPost]
[Route("Add")]
public string AddRecord([FromBody]ProductionUpload data)
{
string recaptcha = string.Empty;
try
{
console.log("Validate the model");
if (!ModelState.IsValid)
{
throw new Exception("Server-side validation message(s): " +
ModelState.Values.Select(v => v.Errors.Select(e => e.ErrorMessage).Aggregate((a, b) => a + "; " + b))
.Aggregate((a, b) => a + "; " + b));
}
console.log("Validate Recaptcha response");
if (!ValidateRecaptchaResponse(data.Recaptcha))
{
throw new Exception("Invalid Recaptcha response: " + data.Recaptcha);
}
console.log("Save the request to DB");
data.DateOfSubmission = DateTime.UtcNow;
_context.ProductionUploads.Add(data);
_context.SaveChanges();
data.RegistryNumber = "P" + data.Id.ToString().PadLeft(6, '0');
_context.SaveChanges();
recaptcha = data.Recaptcha;
}
catch (Exception ex)
{
SendExceptionEmail(ex);
_logger.LogError("{0}\n{1}", ex.Message, ex.StackTrace);
throw;
}
return recaptcha;
}
[HttpPost]
[DisableRequestSizeLimit]
[Route("Upload")]
public void UploadDocument()
{
List<ProductionUpload> fileData = new List<ProductionUpload>();
try
{
var recaptcha = Request.Form["data"].ToString();
var files = Request.Form.Files;
console.log("Validate the document");
if (files.Count > 0)
{
uint maximumDocumentSize = _configuration.GetSection("AppSettings").GetValue<uint>("MaximumDocumentSize");
List<FileType> allowedDocumentTypes = _configuration.GetSection("AppSettings:AllowedDocumentTypes").Get<List<FileType>>();
var fileExt = Path.GetExtension(files[0].FileName);
if (files[0].Length == 0 || files[0].Length > maximumDocumentSize ||
(!allowedDocumentTypes.Any(ft => ft.Extension.ToLower() == fileExt.ToLower() && (string.IsNullOrEmpty(ft.ContentType) || ft.ContentType.ToLower() == files[0].ContentType.ToLower()))))
{
throw new Exception(string.Format("Server-side file validation failure: {0}, {1} bytes, {2} content type", files[0].FileName, files[0].Length, files[0].ContentType));
}
}
console.log("Validate the cover letter");
if (files.Count > 1)
{
uint maximumCoverLetterSize = _configuration.GetSection("AppSettings").GetValue<uint>("MaximumCoverLetterSize");
List<FileType> allowedCoverLetterTypes = _configuration.GetSection("AppSettings:AllowedCoverLetterTypes").Get<List<FileType>>();
var fileExt = Path.GetExtension(files[1].FileName);
if (files[1].Length == 0 || files[1].Length > maximumCoverLetterSize ||
(!allowedCoverLetterTypes.Any(ft => ft.Extension.ToLower() == fileExt.ToLower() && (string.IsNullOrEmpty(ft.ContentType) || ft.ContentType.ToLower() == files[1].ContentType.ToLower()))))
{
throw new Exception(string.Format("Server-side file validation failure: {0}, {1} bytes, {2} content type", files[1].FileName, files[1].Length, files[1].ContentType));
}
}
console.log("Get the previously saved Submission from the database");
ProductionUpload productionUpload = _context.ProductionUploads.SingleOrDefault(sr => sr.Recaptcha == recaptcha);
if (productionUpload == null || productionUpload.Id == 0)
{
throw new Exception("Invalid Recaptcha response: " + recaptcha);
}
console.log("Save the uploaded files");
string fileUploadPath = _configuration.GetSection("AppSettings").GetValue<string>("FileUploadPath");
string pathToSave = Path.Combine(fileUploadPath, Guid.NewGuid().ToString());
if (!Directory.Exists(pathToSave))
Directory.CreateDirectory(pathToSave);
for (int i = 0; i < files.Count; i++)
{
var fileName = Path.GetFileName(files[i].FileName);
var localFileName = ReplaceInvalidCharacters(fileName);
var dbPath = Path.Combine(pathToSave, localFileName);
using (var stream = new FileStream(dbPath, FileMode.Create))
{
files[i].CopyTo(stream);
}
if (i == 0)
{
productionUpload.Documents = dbPath;
}
else
{
productionUpload.CoverLetter = dbPath;
}
}
_context.SaveChanges();
}
catch (Exception ex)
{
SendExceptionEmail(ex);
_logger.LogError("{0}\n{1}", ex.Message, ex.StackTrace);
throw;
}
}
[HttpPost]
[Route("Notify")]
public string Notify([FromBody]string recaptcha)
{
string registryNumber = string.Empty;
try
{
console.log("Get the previously saved Submission from the database");
ProductionUpload productionUpload = _context.ProductionUploads.SingleOrDefault(sr => sr.Recaptcha == recaptcha);
if (productionUpload == null || productionUpload.Id == 0)
{
throw new Exception("Invalid Recaptcha response: " + recaptcha);
}
console.log("Generate a PDF version of the submission form");
string templateHtml = GetPdfHtml(productionUpload);
string pdfFilePath = GeneratePdfFile(templateHtml, productionUpload.RegistryNumber);
SendEmail(productionUpload.ContactName, productionUpload.Email, productionUpload.RegistryNumber, pdfFilePath);
registryNumber = productionUpload.RegistryNumber;
}
catch (Exception ex)
{
SendExceptionEmail(ex);
_logger.LogError("{0}\n{1}", ex.Message, ex.StackTrace);
throw;
}
return registryNumber;
}
public static string ReplaceInvalidCharacters(string fileName)
{
string replacementCharacter = "_";
Regex pattern = new Regex("[;:*?\"<>|&',+@!$=%~()]");
string newFileName = pattern.Replace(fileName, replacementCharacter);
return newFileName;
}
private string GetPdfHtml(ProductionUpload data)
{
string templateHtml = System.IO.File.ReadAllText(
Path.Combine(_environment.ContentRootPath, _configuration.GetSection("AppSettings").GetValue<string>("PDFTemplate")));
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
DateTime dateOfSubmission = TimeZoneInfo.ConvertTimeFromUtc(data.DateOfSubmission, timeZoneInfo);
templateHtml = templateHtml.Replace(DateTimePDF, dateOfSubmission.ToString("dd/MM/yyyy hh:mm:ss tt"));
templateHtml = templateHtml.Replace(Id, data.RegistryNumber.ToString());
templateHtml = templateHtml.Replace(ProducingEntity, data.ProducingEntity);
templateHtml = templateHtml.Replace(PartyCode, data.PartyCode);
templateHtml = templateHtml.Replace(RepresentedBy, data.RepresentedBy);
templateHtml = templateHtml.Replace(ContactName, data.ContactName);
templateHtml = templateHtml.Replace(Email, data.Email);
templateHtml = templateHtml.Replace(ContactNumber, data.ContactNumber);
templateHtml = templateHtml.Replace(ReferenceNumber, data.ReferenceNumber);
templateHtml = templateHtml.Replace(NoticeToProduceNumber, data.NoticeToProduceNumber);
templateHtml = templateHtml.Replace(ReplacementFiles, data.ReplacementFiles == 1 ? "Yes" : data.ReplacementFiles == 0 ? "No" : string.Empty);
templateHtml = templateHtml.Replace(UploadMaterial, data.UploadMaterial);
templateHtml = templateHtml.Replace(TrancheNumber, data.TrancheNumber);
templateHtml = templateHtml.Replace(Password, data.Password);
templateHtml = templateHtml.Replace(Description, string.IsNullOrEmpty(data.Description) ? string.Empty : data.Description.Replace("\n", "<br />"));
templateHtml = templateHtml.Replace(CoverLetter, string.IsNullOrEmpty(data.CoverLetter) ? string.Empty : Path.GetFileName(data.CoverLetter));
templateHtml = templateHtml.Replace(Documents, Path.GetFileName(data.Documents));
return templateHtml;
}
private string GeneratePdfFile(string html, string docId)
{
string filePath = null;
console.log("create the HTML to PDF converter");
HtmlToPdf htmlToPdfConverter = new HtmlToPdf();
htmlToPdfConverter.SerialNumber = _configuration.GetSection("AppSettings").GetValue<string>("HTMLtoPDFKey");
console.log("Setting marins");
htmlToPdfConverter.Document.Margins = new PdfMargins(0, 0, 60, 0);
htmlToPdfConverter.Document.Footer.Enabled = true;
htmlToPdfConverter.Document.Footer.Height = 60;
float pdfPageWidth = htmlToPdfConverter.Document.PageOrientation == PdfPageOrientation.Portrait ? htmlToPdfConverter.Document.PageSize.Width : htmlToPdfConverter.Document.PageSize.Height;
float footerHeight = htmlToPdfConverter.Document.Footer.Height;
PdfHtmlWithPlaceHolders htmlWithPageNumbers = new PdfHtmlWithPlaceHolders(-30, footerHeight - 50, "<div style=\"float:right;\"><span style =\"font-size: 11pt; color: black;font-family:Calibri;\">Page {CrtPage} of {PageCount}</span></div>", null);
htmlToPdfConverter.Document.Footer.Layout(htmlWithPageNumbers);
htmlToPdfConverter.PageCreatingEvent += new PdfPageCreatingDelegate(htmlToPdfConverter_PageCreatingEvent);
try
{
console.log("convert HTML to PDF");
byte[] pdfBuffer = htmlToPdfConverter.ConvertHtmlToMemory(html, null);
string path1 = Path.Combine(_configuration.GetSection("AppSettings").GetValue<string>("SubmissionDataPath"));
if (!Directory.Exists(path1))
Directory.CreateDirectory(path1);
filePath = Path.Combine(path1, docId + ".pdf");
System.IO.File.WriteAllBytes(filePath, pdfBuffer);
}
finally
{
htmlToPdfConverter.PageCreatingEvent -= new PdfPageCreatingDelegate(htmlToPdfConverter_PageCreatingEvent);
}
return filePath;
}
private void htmlToPdfConverter_PageCreatingEvent(PdfPageCreatingParams eventParams)
{
PdfPage pdfPage = eventParams.PdfPage;
pdfPage.DisplayFooter = true;
}
'appsettings.json'
"AllowedFileTypes": [
{
"ContentType": "application/pdf",
"Extension": ".pdf"
},
{
"ContentType": "",
"Extension": ".rtf"
},
{
"ContentType": "",
"Extension": ".txt"
},
{
"ContentType": "application/msword",
"Extension": ".doc"
},
{
"ContentType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"Extension": ".docx"
},
{
"ContentType": "application/vnd.ms-powerpoint",
"Extension": ".ppt"
},
{
"ContentType": "application/vnd.ms-powerpoint",
"Extension": ".pps"
},
{
"ContentType": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"Extension": ".pptx"
},
{
"ContentType": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"Extension": ".ppts"
},
{
"ContentType": "application/vnd.ms-excel",
"Extension": ".xls"
},
{
"ContentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Extension": ".xlsx"
},
{
"ContentType": "application/vnd.oasis.opendocument.text",
"Extension": ".odt"
},
{
"ContentType": "application/vnd.oasis.opendocument.spreadsheet",
"Extension": ".ods"
},
{
"ContentType": "application/vnd.oasis.opendocument.presentation",
"Extension": ".odp"
},
{
"ContentType": "",
"Extension": ".wpd"
},
{
"ContentType": "",
"Extension": ".qpw"
},
{
"ContentType": "",
"Extension": ".shw"
},
{
"ContentType": "application/x-iwork-pages-sffpages",
"Extension": ".pages"
},
{
"ContentType": "application/x-iwork-numbers-sffnumbers",
"Extension": ".numbers"
},
{
"ContentType": "application/x-iwork-keynote-sffkey",
"Extension": ".key"
},
{
"ContentType": "image/jpeg",
"Extension": ".jpg"
},
{
"ContentType": "image/jpeg",
"Extension": ".jpeg"
},
{
"ContentType": "image/tiff",
"Extension": ".tif"
},
{
"ContentType": "image/tiff",
"Extension": ".tiff"
},
{
"ContentType": "image/png",
"Extension": ".png"
},
{
"ContentType": "",
"Extension": ".heic"
},
{
"ContentType": "",
"Extension": ".heif"
},
{
"ContentType": "",
"Extension": ".wav"
},
{
"ContentType": "",
"Extension": ".mp3"
},
{
"ContentType": "",
"Extension": ".m4a"
},
{
"ContentType": "",
"Extension": ".oga"
},
{
"ContentType": "",
"Extension": ".ogg"
},
{
"ContentType": "",
"Extension": ".ogv"
},
{
"ContentType": "",
"Extension": ".flac"
},
{
"ContentType": "",
"Extension": ".wma"
},
{
"ContentType": "",
"Extension": ".wmv"
},
{
"ContentType": "",
"Extension": ".mov"
},
{
"ContentType": "",
"Extension": ".avi"
},
{
"ContentType": "",
"Extension": ".mpeg"
},
{
"ContentType": "",
"Extension": ".mpg"
},
{
"ContentType": "",
"Extension": ".mp4"
},
{
"ContentType": "",
"Extension": ".m4v"
},
{
"ContentType": "",
"Extension": ".3gp"
},
{
"ContentType": "",
"Extension": ".3g2"
}
],
"MaximumFileSize": "4294967295",
有时这是网络问题导致的吗?
解决方案
推荐阅读
- python - tf.ones 返回零而不是一
- javascript - 指定时间后的杀戮功能
- sql - PostgreSQL 行安全策略不适用于 CREATE POLICY FOR UPDATE WITH CHECK (false);
- javascript - 在节点中导入 ES6 相关模块时找不到模块
- apache-kafka - 运行单个 Zookeeper 实例是否安全?
- dataweave - 与 Dataweave 相关的问题
- highcharts - Highcharts 如何根据值更改数据标签的对齐方式
- html - 当我调整屏幕大小时,如何让盒子相互包裹?(弹性盒)
- arrays - 为什么 scipy.optimize.curve_fit 需要多个数组作为输入?
- html - 如何使用 box-decoration-break 填充背景颜色直到行尾?