Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying a progress bar showing the progress of a form submission

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

like image 936
AlexB Avatar asked Feb 20 '14 08:02

AlexB


People also ask

Which bar display the progress bar?

In this example, modeless progress is shown in the address bar. Otherwise, if the window has a status bar, display the modeless progress in the status bar. Put any corresponding text to its left in the status bar. In this example, modeless progress is shown in the status bar.


1 Answers

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.

like image 197
acarlon Avatar answered Sep 21 '22 13:09

acarlon