C# 在 ASP.NET MVC 4 架构注意事项中上传和处理 CSV 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13710260/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Upload and Process CSV File in ASP.NET MVC 4 Architectural Considerations
提问by TNCodeMonkey
I am working on an ASP.NET MVC 4 Application that imports and processes a CSV file. I am using a standard form and controller for the upload. Here is an overview of what I am doing currently:
我正在开发一个导入和处理 CSV 文件的 ASP.NET MVC 4 应用程序。我正在使用标准表单和控制器进行上传。以下是我目前正在做的事情的概述:
Controller Logic
控制器逻辑
public ActionResult ImportRecords(HttpPostedFileBase importFile){
var fp = Path.Combine(HttpContext.Server.MapPath("~/ImportUploads"), Path.GetFileName(uploadFile.FileName));
uploadFile.SaveAs(fp);
var fileIn = new FileInfo(fp);
var reader = fileIn.OpenText();
var tfp = new TextFieldParser(reader) {TextFieldType = FieldType.Delimited, Delimiters = new[] {","}};
while(!tfp.EndOfData){
//Parse records into domain object and save to database
}
...
}
HTML
HTML
@using (Html.BeginForm("ImportRecords", "Import", FormMethod.Post, new { @id = "upldFrm", @enctype = "multipart/form-data" }))
{
<input id="uploadFile" name="uploadFile" type="file" />
<input id="subButton" type="submit" value="UploadFile" title="Upload File" />
}
The import file can contain a large number of records (average 40K+) and can take quite some time to complete. I'd rather not have a user sitting at the import screen for 5+ minutes for each file processed. I have considered adding a console application to watch the uploads folder for new files, and process when something new is added, but would like to see what input I receive from the community before starting my journey down this path.
导入文件可能包含大量记录(平均 40K+),并且可能需要相当长的时间才能完成。对于每个处理的文件,我不想让用户在导入屏幕上停留 5 分钟以上。我曾考虑添加一个控制台应用程序来查看新文件的上传文件夹,并在添加新文件时进行处理,但希望在开始我的旅程之前查看我从社区收到的输入。
Is there a more efficient way to handle this operation?
有没有更有效的方法来处理这个操作?
Is there a way to perform this action, allowing the user to continue about his/her merry way, and then notify the user when processing is done?
有没有办法执行这个动作,让用户继续他/她的快乐方式,然后在处理完成时通知用户?
采纳答案by TNCodeMonkey
The solution to the issue I was having is a bit complex, but works similar to the IFrame fix. The result is a pop-up window that handles the processing, allowing the user to continue navigation throughout the site.
我遇到的问题的解决方案有点复杂,但与 IFrame 修复类似。结果是一个处理处理的弹出窗口,允许用户继续在整个站点中导航。
The file is submitted to the server (UploadCSV controller), a Success page is returned with a bit of JavaScript to handle the initial kick-off of the processing. When the user clicks "Begin Processing", a new window is opened (ImportProcessing/Index) that loads the initial status (kicking off an interval loop that retrieves status updates) and then makes a call to the "StartProcessing" action, kicking off the processing process.
文件被提交到服务器(UploadCSV 控制器),返回一个带有一些 JavaScript 的成功页面来处理处理的初始启动。当用户单击“开始处理”时,将打开一个新窗口(ImportProcessing/Index),该窗口加载初始状态(启动检索状态更新的间隔循环),然后调用“StartProcessing”操作,启动处理过程。
The "FileProcessor" class that I am using is housed in a static dictionairy variable within the ImportProcessing controller; allowing for status results based on the key. The FileProcessor is promptly removed after the operation is complete or an error is encountered.
我正在使用的“FileProcessor”类位于 ImportProcessing 控制器内的静态字典变量中;允许基于密钥的状态结果。FileProcessor 会在操作完成或遇到错误后立即删除。
Upload Controller:
上传控制器:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadCSV(HttpPostedFileBase uploadFile)
{
var filePath = string.Empty;
if (uploadFile.ContentLength <= 0)
{
return View();
}
filePath = Path.Combine(Server.MapPath(this.UploadPath), "DeptartmentName",Path.GetFileName(uploadFile.FileName));
if (new FileInfo(filePath).Exists)
{
ViewBag.ErrorMessage =
"The file currently exists on the server. Please rename the file you are trying to upload, delete the file from the server," +
"or contact IT if you are unsure of what to do.";
return View();
}
else
{
uploadFile.SaveAs(filePath);
return RedirectToAction("UploadSuccess", new {fileName = uploadFile.FileName, processType = "sonar"});
}
}
[HttpGet]
public ActionResult UploadSuccess(string fileName, string processType)
{
ViewBag.FileName = fileName;
ViewBag.PType = processType;
return View();
}
Upload Success HTML:
上传成功 HTML:
@{
ViewBag.Title = "UploadSuccess";
}
<h2>File was uploaded successfully</h2>
<p>Your file was uploaded to the server and is now ready to be processed. To begin processing this file, click the "Process File" button below.
</p>
<button id="beginProcess" >Process File</button>
<script type="text/javascript">
$(function () {
$("#beginProcess").click(BeginProcess);
function BeginProcess() {
window.open("/SomeController/ImportProcessing/[email protected]&[email protected]", "ProcessStatusWin", "width=400, height=250, status=0, toolbar=0, scrollbars=0, resizable=0");
window.location = "/Department/Import/Index";
}
});
</script>
Once this new window is opened up, the file processing begins. Updates are retrieved from a custom FileProcessing class.
一旦这个新窗口打开,文件处理就开始了。从自定义 FileProcessing 类中检索更新。
ImportProcessing Controller:
导入处理控制器:
public ActionResult Index(string fileName, string type)
{
ViewBag.File = fileName;
ViewBag.PType = type;
switch (type)
{
case "somematch":
if (!_fileProcessors.ContainsKey(fileName)) _fileProcessors.Add(fileName, new SonarCsvProcessor(Path.Combine(Server.MapPath(this.UploadPath), "DepartmentName", fileName), true));
break;
default:
break;
}
return PartialView();
}
ImportProcessing Index:
进口加工指数:
@{
ViewBag.Title = "File Processing Status";
}
@Scripts.Render("~/Scripts/jquery-1.8.2.js")
<div id="StatusWrapper">
<div id="statusWrap"></div>
</div>
<script type="text/javascript">
$(function () {
$.ajax({
url: "GetStatusPage",
data: { fileName: "@ViewBag.File" },
type: "GET",
success: StartStatusProcess,
error: function () {
$("#statusWrap").html("<h3>Unable to load status checker</h3>");
}
});
function StartStatusProcess(result) {
$("#statusWrap").html(result);
$.ajax({
url: "StartProcessing",
data: { fileName: "@ViewBag.File" },
type: "GET",
success: function (data) {
var messag = 'Processing complete!\n Added ' + data.CurrentRecord + ' of ' + data.TotalRecords + " records in " + data.ElapsedTime + " seconds";
$("#statusWrap #message").html(messag);
$("#statusWrap #progressBar").attr({ value: 100, max: 100 });
setTimeout(function () {
window.close();
}, 5000);
},
error: function (xhr, status) {
alert("Error processing file");
}
});
}
});
</script>
Finally the Status Checker html:
最后是状态检查器 html:
@{
ViewBag.Title = "GetStatusPage";
}
<h2>Current Processing Status</h2>
<h5>Processing: @ViewBag.File</h5>
<h5>Updated: <span id="processUpdated"></span></h5>
<span id="message"></span>
<br />
<progress id="progressBar"></progress>
<script type="text/javascript">
$(function () {
var checker = undefined;
GetStatus();
function GetStatus() {
if (checker == undefined) {
checker = setInterval(GetStatus, 3000);
}
$.ajax({
url: "[email protected]",
type: "GET",
success: function (result) {
result = result || {
Available: false,
Status: {
TotalRecords: -1,
CurrentRecord: -1,
ElapsedTime: -1,
Message: "No status data returned"
}
};
if (result.Available == true) {
$("#progressBar").attr({ max: result.Status.TotalRecords, value: result.Status.CurrentRecord });
$("#processUpdated").text(result.Status.Updated);
$("#message").text(result.Status.Message);
} else {
clearInterval(checker);
}
},
error: function () {
$("#statusWrap").html("<h3>Unable to load status checker</h3>");
clearInterval(checker);
}
});
}
});
</script>
回答by Jesse Carter
Just a thought but you could thread the processing of your CSV files and on completion of that task call another method that basically provides a modal dialog or some kind of javascript alert on the client side letting the user know that the processing has completed.
只是一个想法,但您可以线程化处理 CSV 文件,并在完成该任务后调用另一种方法,该方法基本上在客户端提供模式对话框或某种 javascript 警报,让用户知道处理已完成。
Task.Factory.StartNew(() => ProcessCsvFile(fp)).ContinueWith((x) => NotifyUser());
or something along those lines. I think that ultimately you are gonna wanna look at some kind of threading because it certainly does not make sense for a user to be stuck looking at a screen while some kind of server side processing takes place.
或类似的规定。我认为最终你会想要看看某种线程,因为当某种服务器端处理发生时,用户被困在屏幕上当然没有意义。

