2

I have the following code which is used to upload large files (~6MB) to an ASP .NET MVC2 application. After the file is uploaded it can take 5 minutes to return a response from the controller.

During this time, IE9 does not show any sign that processing is not yet completed, i.e. user can select other file and re-post.

How do you inform the user that an operation is not completed and disable the submit button until the old file processing is completed?

<% using (Html.BeginForm("InvoiceImport", "Grid", 
               FormMethod.Post, new { enctype = "multipart/form-data" }))
{%>
<input name="uploadFile" type="file" />
<input type="submit" name='invoice' value='Read'/>
<%} %>

Controller:

[AcceptVerbs(HttpVerbs.Post)]
[Authorize]
ActionResult ImportFromFile(HttpPostedFileBase uploadFile)
{
    Server.ScriptTimeout = 30 * 60;
    var res = ImportFromFile(uploadFile.InputStream); // takes lot of time
    TempData["Message"] = uploadFile.FileName + " Readed records " + res;
    return RedirectToAction("Complete");
}

Update

I looks like the most important issue is to prevent duplicate submits. I tried code below to prevent duplicate submits but for unknown reason Session["Upload"] is null if submit button is pressed in second time. How to prevent duplicate submits ?

View added

 <% if (TempData["Message"]!=null) { %>
   setTimeout( function() {
     showMessage ( '<%= TempData["Message"] as string %>');
    }, 300 );
    <% } %>


Controller

ActionResult ImportFromFile(HttpPostedFileBase uploadFile)
    {

        if (Session["Upload"] != null)
        {
            // todo: why this point is never reached ?
            TempData["Message"] = Session["Upload"] + " Please wait file is being processed";
            return View();
        }

        Session.Add("Upload", uploadFile.FileName);
        Server.ScriptTimeout = 30 * 60;
        ImportFromFile(uploadFile.InputStream);
        TempData["Message"] = uploadFile.FileName + " completed";
        Session.Remove("Upload");
        return RedirectToAction("Complete");
    }
Andrus
  • 26,339
  • 60
  • 204
  • 378

4 Answers4

1

While using an asyncronus call, display to the user that the call is in progress. Check this blog post to learn about long polling, which would allow you to make a call that returns when the process finishes.

  1. Display to the user that they've submitted the form and disable the submit button.
  2. Make the Ajax call to actually make the call and do the upload.
  3. When it finishes inform the user that the job has finished.

If you're worried they'd refresh the page and therefore be able to submit again, you would need to keep track of their submission status on the back end. Some sort of lock so you would know that the user has a running upload and that they should not be able to upload again.

petebowden
  • 900
  • 2
  • 8
  • 21
  • Thank you very much. Yes, duplicate submit issue seems to be most important. I tried to use Session buit for unknow reason it fails since Session key is not saved in session. How to fix this ? I updated question. – Andrus Jul 02 '12 at 20:26
  • 1
    @Andrus Can you debug and tell me what the value of Session["Upload"] is when you expect it to enter that if statement? – petebowden Jul 03 '12 at 01:21
  • After `Session.Add("Upload", uploadFile.FileName);` `Session["Upload"]` contains file name. If I put breakpoint to start of ImportFile and press F5 `Session["Upload"]` is null. It looks like session variable is not passed in next call. How to fix this ? – Andrus Jul 03 '12 at 08:30
  • @Andus, as Michael said ASP .NET does not allow you to have multiple sessions open at once (bummer). His alternative solutions should work, anything that will store some sort of unique identifier. You could even use cookies, although it's possible the user might modify them or even not allow them. But if you don't have a database or any way to write to a filesystem it would work. But your best bet is to write to a file somewhere, and then check against that value. Once you do that you could use AJAX to tell them when the upload is complete. – petebowden Jul 03 '12 at 12:24
1

The simplest solution to your problem is to disable the button and fields using javascript. Do you have any javascript libraries like jQuery in our page?

$('form').submit(function(){
   $(this).find('input').prop('disabled', true)
});

Something like that for jQuery. If you have several forms you might want to add an id to the form to target only this one.

UPDATE:

As the problem with resubmitting there are no dead simple solution:

To avoid the F5 problem. Make sure to redirect after the POST. Which you do, but I guess if it takes long time they might press F5 meanwhile.

A few solutions on top of my head to this problem.

  • (IF you are using SQL) Generate a guid that you place in a hidden field on the form page. Make this a unique column in the database and insert it with the rest of the information. If they try to resubmit SQL will protect you by throwing an exception you can handle.

  • There are alternatives to the above technique. Maybe you store those guids in a separate table and check against that or maybe in XML or cache or whatever.

  • When you submit the form add a timestamp with javascript. The first thing you do on the server is to make sure you are within x seconds from when the button was clicked.

  • While submiting (After the first submit) you could add a warning with javascript.

    window.onbeforeunload = function(){ return 'Already saving! Please press no'; };

This last solution is not safe att all but very easy to add.

Mikael Eliasson
  • 5,157
  • 23
  • 27
  • Than you very much. Jquery and jqueryUI are used. The most important issue is to prevent duplicate submits. Use cam press F5 and select retry. jquery cannot block this. I updated question and added code which should block duplicate submits but fails. How to fix this ? – Andrus Jul 02 '12 at 20:23
  • Updated the answer with some suggestions – Mikael Eliasson Jul 03 '12 at 07:19
  • Is it possible to use Session[] as in updated part of question ? Why this fails? – Andrus Jul 03 '12 at 10:16
  • You are removing the value: Session.Remove("Upload"); ASP.NET will per default never execute two requests for the same session in parallel. The second request will be run after the first in completed and then you already removed the value – Mikael Eliasson Jul 03 '12 at 10:43
  • @MikaelEliasson I did find this [article](http://msdn.microsoft.com/en-us/library/ms178581) on states, see the last part on concurrent reads. I guess it's possible he could read the state in a read only mode. He'd have to make an ajax call to set the value in his session variable, and the make sure that thread was finished. Then call both the upload and a status method with the read only parameter. – petebowden Jul 03 '12 at 15:18
0

You should consider using an Asyncronus controller.

That way it will run the upload in a seperate thread and the page will no longer lock up.

Check out the following link:

Using an Asychronous Controller

Gaz Winter
  • 2,924
  • 2
  • 25
  • 47
  • Thank you. I created custom controller to prevent duplicate submits as show in updated part of question but it fails since Session["Upload"] is null for unknows reason. How to fix this, can Asynchronous Controller help or other idea ? – Andrus Jul 02 '12 at 20:28
0

Consider using some ajaxified upload solutions where you will be able to show the progress of the process to the user till it got finished.

This SO answer gives detailed example of using Valums ajax uploaded. Personally Darin like this solution. So it should be a good one.

Community
  • 1
  • 1
Shyju
  • 214,206
  • 104
  • 411
  • 497
  • Thank you. Ajax cannot prevent duplicate submit if F5 and retry is pressed. I tried code in updated part of question but it fails. How to prevent duplicate submits? – Andrus Jul 02 '12 at 20:25