2012-12-04 4 views
7

CSVファイルをインポートして処理するASP.NET MVC 4アプリケーションを作成しています。私はアップロードのために標準的なフォームとコントローラを使用しています。ここで私は現在やっていることの概要です:ASP.NET MVCでのCSVファイルのアップロードと処理4建築上の考察

コントローラロジックは

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 
    } 
    ... 
} 

インポートファイルが多数のレコードを含めることができ

@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" /> 
} 

HTML(平均40K +)完了するまでにかなりの時間がかかることがあります。私はむしろ、処理された各ファイルのために5分以上の間、ユーザーがインポート画面に座っていらっしゃることはありません。私はコンソールアプリケーションを追加して新しいファイルのアップロードフォルダを監視し、新しいものが追加されたときに処理することを検討しましたが、このパスを使って私の旅を開始する前にコミュニティから受け取った入力を確認したいと思います。

この操作をより効率的に処理する方法はありますか?

この操作を実行する方法はありますか?ユーザーは気楽なやり方を続け、処理が完了したらユーザーに通知できますか?

答えて

10

私が持っていた問題の解決策は少し複雑ですが、IFrame修正プログラムと同様に動作します。その結果、処理を処理するポップアップウィンドウが表示され、ユーザーはサイト全体のナビゲーションを継続できます。

ファイルがサーバー(UploadCSVコントローラ)に送信されると、処理の初期キックオフを処理するJavaScriptのビットとともにSuccessページが返されます。ユーザーが「Begin Processing」をクリックすると、初期状態をロードする(状態更新を取得するインターバル・ループを開始する)新しいウィンドウが開き(ImportProcessing/Index)、「StartProcessing」アクションが呼び出され、処理プロセス。

私が使用している "FileProcessor"クラスは、ImportProcessingコントローラ内の静的なdictionairy変数に格納されています。そのキーに基づいてステータス結果が得られます。 FileProcessorは、操作が完了した後、またはエラーが発生した後、速やかに削除されます。

アップロードコントローラー:

[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(); 
     } 

アップロード成功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> 

、ファイル処理が開始されます。更新はカスタムFileProcessingクラスから取得されます。

ImportProcessingコントローラー:

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インデックス:

@{ 
    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> 

最後にステータスチェッカーの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> 
0

ちょっと考えましたが、CSVファイルの処理をスレッド化して、そのタスクの完了時に、クライアント側でモーダルダイアログや何らかの種類のjavascriptアラートを基本的に提供する別のメソッドを呼び出すことで、完了しました。

Task.Factory.StartNew(() => ProcessCsvFile(fp)).ContinueWith((x) => NotifyUser()); 

またはこれらの行に沿ったもの。私は最終的には、何らかの種類のサーバーサイド処理が行われている間に、ユーザーが画面を見ていることに意味がないため、何らかのスレッドを見たいと思っています。

+0

の入力をありがとう、私はしばらくの間、これで遊んで、それから出てくるものを参照してください。私がこの答えを受け入れることを確認します。 – TNCodeMonkey

+0

私はこの方法を使用してアプローチする最善の方法を見つけることを試みたが、私が必要としていたものではないことに気づいた。スレッドがスレッドに関係なく、アクションが返されるまでポストが開いたままであるという事実を救済することはできません。私は私の解決策で底に答えを加えました。 – TNCodeMonkey

関連する問題