Displaying a progress bar showing the progress of a form submission

15,013

Solution 1

You can use XMLHttpRequest to receive updates when uploading files. There are also various jquery-type wrappers to achieve the same. I will describe a solution using XMLHttpRequest. First intercept the form submit.

$("#target-form").submit(function () {

Then create XHR request:

 xhr = new XMLHttpRequest();

Then register to be notified about progress events:

xhr.upload.addEventListener( "progress", function ( evt )
{
    if( evt.lengthComputable )
    {
        var progressPercent = ( evt.loaded / evt.total ) * 100;
        showProgress( value );//your function.
    }
}, false );


//Some other events you will probably want to subscribe to
xhr.addEventListener( "load", function ()
{            
    fileUploadComplete( this.responseText );
}, false );

xhr.addEventListener( "error", function ( first, second, third )
{
    fileUploadComplete( "Error: Image format not supported." );
}, false );

xhr.addEventListener( "abort", function ()
{
    fileUploadComplete( "Error: Upload was cancelled. Please try again." );
}, false );

Open XHR passing in any arguments (id is shown as an example)

xhr.open( "post", '@Html.Raw( @Url.Action( "UploadImage", new { someId = id } ) )', true );

// Set appropriate headers                
xhr.setRequestHeader( "Content-Type", "multipart/form-data" );
xhr.setRequestHeader( "X-File-Name", name );

// Send the file
xhr.send( file );

Then in the controller:

public ActionResult UploadImage( int someId, HttpPostedFileBase userFile  )
{
    ...    
}

You will receive the updates in the update handler.

Extension for long running server task

If there is some long running task on the server (such as writing the data to the database), then you need to chunk the operations (so that you can give updates on completion of each chunk) on the server and implement code to handle a long running service that you can query from javascript. See here. The basic idea is to start the task on the server side and periodically check the progress from Javascript. You may want to have two separate progress bars, one for uploading and one for the server side operation. This provides more information to the user - they will know that the file upload is complete and now some server-side operation is happening.

Solution 2

Add an ajax timer control which checks progress with the server and set time interval (say every 2 seconds).

Execute your server task on a new thread. This will cause your post back to return immediately whilst executing your long task in the background. Start the ajax timer.

As the long task works let it update a session variable which contains the % work done.

When the timer postbacks to the sever, fetch the % work done from the session variable.

You can design a progress bar by having an inner div and an outer div. Set the width of the inner div to the % work done: $('#progressBarWorkDone').css('width', WorkDone + '%');

<div id="progressBar" style="width:400px;">    
<div id="progressBarWorkDone" style="width:0px;height:10px;background-color:red;"><div/> 
<div/>

Note, if you don't know how to use the timer control, you can also execute AJAX calls from javascript using setInterval(function(){ AJAX CALLBACK GOES HERE },1000);

However having said all that it way simpler to just put an animated gif inside your progress bar div. Hide the div, and then show the div when the submit button is clicked.

<input type="submit" onclick="$('#progressBar').show();" value="Create" class="submitButton" />

<div id="progressBar" style="display:none;"><img src="myProgressBar.gif" /></div>
Share:
15,013
AlexB
Author by

AlexB

I'm a full-stack developer experienced in ASP.NET MVC. I use to work with SQL, C# and some front-end JS frameworks. Currently, I'm working in Switzerland as a .NET engineer.

Updated on June 15, 2022

Comments

  • AlexB
    AlexB almost 2 years

    This question is not totally answered, please fell free to contribute !


    I'm trying to display a simple progress bar while a large form is submitted.

    The form contains a dozen of fields, plus some file upload fields, where the user can select a picture. Then, when he clicks on a Create button, the form with data and pictures are submitted and the entity is created in DB. (only one click to submit the form AND the pictures).
    Everything works fine, but I'd like to display a progress bar during the submit process.

    I have found a lot of tutorials explaining how to display a progress bar, but I don't find anyone explaining how to display a progress bar indicating the percentage of work accomplished by a method, ie, I'd like to see 10%, 25%, etc... during the submit process.

    So, basically, this is what I've done : (this is an ASP.NET MVC3 project)

    @model MyModel
    
    @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "target-form", enctype = "multipart/form-data" }))
    {
        //some Html.TextBoxFor() and other @Html.DropDownListFor
    
        @Html.TextBoxFor(m => m.File, new { type = "file"})
    
        <input type="submit" value="Create" class="submitButton" />
    
        <div id="progressBar"></div>
    }
    

    And a basic controller

    [HttpPost]
    public ActionResult Create(MyModel model)
    {
        if (ModelState.IsValid)
        {
            DALLayer dal = new DALLayer()
            dal.AddEntity(model);
    
            return RedirectToAction("Index", "Home");
        }
    
        return null;
    }
    

    Is it possible to transform my last <div> in a progressBar displaying the state of upload progress ?


    Here are my requirements :

    • No plugin (this is a personnal project, I want to understand how to do this by myself).
    • Cross compatible, IE8+ (if possible)
    • jQuery ok, but no Flash.


    Thank you very much !

    UPDATE 1 Here is a JSFIDDLE, where I'm trying to adapt this link but without success... If you think you can help, you're welcome !


    UPDATE 2 Ok, I used acarlon's answer to submit my form with XMLHttpRequest and the datas are correcty posted to the controller. However, the ProgressBar still doesn't appear !
    I just replace the data passed to the controller by :

    formData = new FormData(document.getElementById("target-form") );
    xhr.open("POST", "@Url.Action("MyMethod", "MyController")", true );
    

    and try some different headers, like :

    xhr.setRequestHeader("X-File-Name", $('#Files_0__File')[0].files[0].name);
    xhr.setRequestHeader("X-File-Size", $('#Files_0__File')[0].files[0].size);
    xhr.setRequestHeader("X-File-Type", $('#Files_0__File')[0].files[0].type);
    //Also tried with "Content-Length" but it doesn't care about it.
    

    (It was hardcoded here to be sure it has good values. I'll do it in a loop when I'll be more at ease with it.)

    And when I submit my form, the XMLHttpRequest send has these fields :

    readyState: 4
    status: 0 <= should be 200, right ?

    And in the error handler, I have these values :

    loaded: 0
    total: 883526
    type: "error"

    So the data are submitted to my controller, but I'm unable to display this damned progressbar...