Facebook

Sunday, July 1, 2018

Performance Optimization of Relational Database

Case study based on the experience in performance enhancement of an application done with Qburst Technologies Pvt Ltdhttp://www.qburst.com/

MySql Based Applications - Improve Performance with Application Level Optimization


MySql is one of the data base solution used by most of the small and medium scale organization for their web applications. MySql delivers good performance with well designed table structures an relations. Most web applications deals with mysql performance issues when volume of data grows beyond expectations.
Usually when we design web based applications with mySql in the backend we follow normalized design pattern with look-up tables and foreign key relations. This kind of normalized design ensures good performance and less read write time when the data volume is within expected limits. (ie, practically there is an upper limit on volume of data that can be handled with relational databases and is defined by type of data and nature of data access).
Data volume within limit:
Usually large volumes of data are populated when we deal with real world scenario like daily activities, stock market data, social media data etc which are usually dependent on human interactions. We can predict user behavior only upto a limit and so is the volume of data being populated in such scenarios.

When data volume grows:
If we go for normalized design, we should keep similar kind of data in same place and searching for mutually related data may force us to go for self joins in the heavily populated tables. Self join of heavily populated tables is going to be a big issue in the performance point of view. When data volume grows the normalized structure may not deliver expected performance due to many factors.
Barriers:
One of them is foreign key relations and another is the normalized structure itself. We use foreign keys to keep track of relational data kept in normalized tables, which can cause issues like increased insert/update time caused by the overhead on the database to create and maintain indexes.
Alternatives:
There is a cap on volume of data that can be effectively handled by mysql with normalized structure, which force us to think about alternatives like non-relational database like mongo db. An issue we are going to face with non-relational database is handling relation of mutually dependent real world data. There are ways to tackle the situation.
Budget:
Even though non relational databases promise faster reading, there are issues like poor write speed. Obviously there are techniques to tackle the issues in non relational database, most of them are heavy in terms of cost and/or effort for small and medium scale organizations.
Economic alternatives:
The main question that gains popularity is "Is it possible to extend the practical upper limit on volume of data that can be handled effectively?" and the answer is YES
We can increase the upper limit on volume of data that can be handled with mysql by following some techniques like de-normalization, archiving etc.
MySql can respond to normal select queries better compared to select queries including joins with same or other tables.
De-normalization can help improve read time in many ways. One of them is keeping data in a purpose oriented manner. De-normalization can improve performance of data read by duplicating data to multiple tables that are meant for display purpose, thus avoiding joins.
For example to make display of data faster, we can keep data optimized for display in a separate table that is used for display regardless of duplication. Now one possible issue is to manage consistency of data. In normalized and properly designed databases, consistency of data is assured by database to a large extend. But as we keep duplicate copies of data for making data read faster, the responsibility of keeping the data kept in multiple locations(data duplication) consistent should be taken from database to your application. This approach can increase the coding effort that take care of data handling and update in multiple copies of data.
How:
De-normalization can help in reducing reading time if we include additional tables that can keep collective data. We can do this by creating rules for data display in all possible scenarios and combined display rules can be handled together with less overhead and duplicate data to multiple tables in a way that each one is optimized for its purpose. This can improve performance of data population at front end, at the same time increasing processing overhead to the code.
Archiving:
Real world data usually become in consistent or less accessed as time pass by. For example if an application manages marketing statistics, data of last 6 months or 1 year may be relevant and previous data becomes less relevant or less frequently accessed. The less relevant or less accessed data can be moved from main database to an archiving system, which can be another database or even tapes or disks based on demand for old data. If the user needs previous data, which can be provided on demand.
Observe your data:
There are techniques that can make mysql perform better but depends on structure and behaviour of data. Structured monitoring can give an idea on the root cause of the problem and based on which proper solution can be formed. Log your data access, create metrics of data access and find data access peeks and performance falls. These peeks and falls can explain the real problem/root cause. Solution can be formed based on this real problem.

Based on optimization tasks I worked on a project for Qburst Technologies Pvt Ltd.

References: 

https://www.toptal.com/sql-server/sql-database-tuning-for-developers


https://www.toptal.com/database/the-definitive-guide-to-nosql-databases

Tuesday, November 28, 2017

Postgress Tips and Tricks


1. Usage: Login to remote postgres.

$: psql -h xxx.xxx.com -U postgres -W

2. Usage: Create database.

$: CREATE DATABASE databaseName;
$: sudo adduser databaseName
$: sudo passwd databaseName
$: su - databaseName
$: psql -d template1 -c "ALTER USER databaseName WITH PASSWORD 'newpassword';"

3. Usage: Common command prompt options.

$: \dt //- list tables
$: \l //- list databases
$: \c //- Connect database
$: \q //- dis-connect database
$: DROP TABLE test; //- delete table;
$: TRUNCATE TABLE profile_events; // Drop table
$: ALTER SEQUENCE seq RESTART WITH 1; //- restart sequence after truncate table
$: UPDATE TABLE SET idcolumn=nextval('seq'); //- update sequence of table

4. Note:

PostgreSQL uses sequences to generate auto-numeric values, by default, Phalcon tries to obtain the generated value from the sequence table_field_seq, for example: robots_id_seq, if that sequence has a different name, the getSequenceName() method needs to be implemented:

Sunday, July 23, 2017

Mercurial: Tips and tricks


1. Usage: Completely remove a named branch.

$: hg strip "branch(${BRANCHNAME})"

2. Now re-iterate for all the branches you have, that's it

$:hg pull -u

3. To check if you have any unwanted changes

$:hg outgoing


4. Add, Remove tag

$:hg tag -r newrevisionhash stable
$:hg tag --remove stable


5. Incoming and Outgoing

$:hg incoming
$:hg incoming -p
$:hg incoming -b <branchname>
$:hg incoming -p -b <branchname>

6. Copy files across branches

$:hg update -r to-branch
$:hg revert -r from-branch file
$:hg ci -m 'copied single file from from-branch to to-branch

7. Dirty merge If both branches have made changes to a file that you want to keep, a dirty trick would be to create a merge of the two branches using hg merge, possibly/probably on still another branch, check that in, and then copy a single file between the merge and the to-branch(Reference: Stackoverflow)

$:hg update -r to-branch
$:branch merge-branch
$:hg merge -r from-branch
$:hg ci -m 'temp merge to be discarded"
$:hg update -r to-branch
$:hg revert -r merge-branch single-file
$:hg ci -m 'merged single-file from from-branch to to-branch"
$:hg strip merge-branch

8. To see heads in a particular branch <branch name>

$:hg heads <branch name>


9. To commit as a user <userDisplayName>

$:hg commit -u <userDisplayName>

Monday, November 28, 2016

Mysqli Bind dynamic parametere

Trying to create a function to insert data into a SQL table, using MySQLI. I want to create a generic function, to insert different types of data in different databases.
The issue is at binding the parameters. I can't find a good way to bind them, as I've got multiple variables with values and keys, but they're all in array format, and  bind_param  requires a new variable for each
$sql = 'SELECT id, lastname FROM customers WHERE ' .
  'category = ? AND ' .
  'lastname LIKE ?';

/* Prepare statement */
$stmt = $conn->prepare($sql); if($stmt === false) {
  trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->errno . ' ' . $conn->error, E_USER_ERROR);
}
 
$category_id = 1;
$lastname = '%Smith%';
 
/* Bind parameters. Types: s = string, i = integer, d = double,  b = blob */
$stmt->bind_param('is', $category_id, $lastname);
 
/* Execute statement */
$stmt->execute();
 
/* Fetch result to array */
$res = $stmt->get_result();
while($row = $res->fetch_array(MYSQLI_ASSOC)) {
  array_push($a_data, $row);
}
The problem
$stmt->bind_param() does not accept params array. So, how to bind params, if their number is variable, depending on user input in your application?

A workaround is to use call_user_func_array to pass dynamically the params array.

The solution
In the following code:

  • $conn is the connection object
  • $a_bind_params is the array of the parameters you want to bind
  • $a_param_type is an array with the type of each parameter (Types: s = string, i = integer, d = double, b = blob). This is another disadvantage of MySQLi API. You have to maintain this array some way in your application.
  • With call_user_func_array, array params must be passed by reference. See notes in manual page.

  • The code:

    /* Bind parameters. Types: s = string, i = integer, d = double,  b = blob */
    $a_params = array();
     
    $param_type = '';
    $n = count($a_param_type);
    for($i = 0; $i < $n; $i++) {
      $param_type .= $a_param_type[$i];
    }
     
    /* with call_user_func_array, array params must be passed by reference */
    $a_params[] = & $param_type;
     
    for($i = 0; $i < $n; $i++) {
      /* with call_user_func_array, array params must be passed by reference */
      $a_params[] = & $a_bind_params[$i];
    }
     
    /* Prepare statement */
    $stmt = $conn->prepare($sql);
    if($stmt === false) {
      trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->errno . ' ' . $conn->error, E_USER_ERROR);
    }
     
    /* use call_user_func_array, as $stmt->bind_param('s', $param); does not accept params array */
    call_user_func_array(array($stmt, 'bind_param'), $a_params);
     
    /* Execute statement */
    $stmt->execute();
     
    /* Fetch result to array */
    $res = $stmt->get_result();
    while($row = $res->fetch_array(MYSQLI_ASSOC)) {
      array_push($a_data, $row);
    }

    Monday, October 24, 2016

    PHP mailer - debug example

    require_once('../class.phpmailer.php');
    //include("class.smtp.php"); // optional, gets called from within class.phpmailer.php if not already loaded
    
    $mail             = new PHPMailer();
    
    $body             = file_get_contents('contents.html');
    $body             = eregi_replace("[\]",'',$body);
    
    $mail->IsSMTP(); // telling the class to use SMTP
    $mail->Host       = "mail.yourdomain.com"; // SMTP server
    $mail->SMTPDebug  = 2;                     // enables SMTP debug information (for testing)
                                               // 1 = errors and messages
                                               // 2 = messages only
    $mail->SMTPAuth   = true;                  // enable SMTP authentication
    $mail->Host       = "mail.yourdomain.com"; // sets the SMTP server
    $mail->Port       = 26;                    // set the SMTP port for the GMAIL server
    $mail->Username   = "yourname@yourdomain"; // SMTP account username
    $mail->Password   = "yourpassword";        // SMTP account password
    
    $mail->SetFrom('name@yourdomain.com', 'First Last');
    
    $mail->AddReplyTo("name@yourdomain.com","First Last");
    
    $mail->Subject    = "PHPMailer Test Subject via smtp, basic with authentication";
    
    $mail->AltBody    = "To view the message, please use an HTML compatible email viewer!"; // optional, comment out and test
    
    $mail->MsgHTML($body);
    
    $address = "whoto@otherdomain.com";
    $mail->AddAddress($address, "John Doe");
    
    $mail->AddAttachment("images/phpmailer.gif");      // attachment
    $mail->AddAttachment("images/phpmailer_mini.gif"); // attachment
    
    if(!$mail->Send()) {
      echo "Mailer Error: " . $mail->ErrorInfo;
    } else {
      echo "Message sent!";
    }
        

    Wednesday, September 7, 2016

    Implement multi file upload using blueimp library

    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -----------HTML ------------- ---------- ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>

    <!-----Image preview area------> <div class="row fileThumbnails">
       <div class="small-2 columns preview hide">
          <img src="#" class="preview-img">
          <button class="cancel" id="" name=""><i class="fa fa-times" aria-hidden="true"></i></button>
       </div>

    </div>
    --- ------------- -----------JAVASCRIPT ----------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })
    Drop and Drop Files Here
    If you want to do some customization in the interface, behaviour etc., I have an example.
    I customised the behaviour as per below requirements.
    Requirement:
    Interface: 
    Create a file selector, 
    That can select multiple files, 
    On selecting files, start uploading images
    Show a single progress bar
    Display a thumbnail of files selected with a contol(may be a cancel button) near to thumbnail preview to cancel upload of that image only

    Blueimp project: https://blueimp.github.io/jQuery-File-Upload/

    ------ ------------- -------------HTML ------------- ------------ ------
    <div id="mulitplefileuploader">
    <div class="ajax-upload-dragdrop text-center">
    <div class="columns small-12">Drop and Drop Files Here</div>
    <div class="columns small-12 drop-drop-img"><img src="{{dragDropURL}}" /></div>
    <div class="columns small-12 medium-6 explicit-small-centered upload-multi-files-button">
    <label for="fileupload" class="button-alert button expanded">
    <img src="{{ uploadIconURL }}"  /> Upload Files
    </label>
    <input id="fileupload" type="file" name="files[]" multiple />
    </div>
    </div>
    </div>
    --- ------------- -------------JAVASCRIPT ------------- ------------ ---
    $(document).ready(function () {
    $('#fileupload').fileupload({
    // This function is called when a file is added to the queue
    add: function (e, data) {

    //This area will contain file list and progress 
    var reader = new FileReader();
    reader.onload = function (e) {
    data.context = data.files[0].name;
    console.log(e.target);
    var imgWrap = $('.fileThumbnails .preview.hide').clone(true);
    $(imgWrap).attr('data-file-name', data.files[0].name);
    $(imgWrap).removeClass('hide').find('img').attr('src', e.target.result);
    $(".fileThumbnails").prepend(imgWrap);
    imgWrap.find('.cancel').click(function () {
    if (!imgWrap.hasClass('finished')) {
    jqXHR.abort();
    imgWrap.remove();
    }
    imgWrap.fadeOut(function () {
    imgWrap.remove();
    });
    });
    }
    reader.readAsDataURL(data.files[0]);

    // Automatically upload the file once it is added to the queue
    var jqXHR = data.submit()
    .success(function (result, textStatus, jqXHR)
    {
    //imgWrap.remove();
    var data = JSON.parse(result);
    //console.log(data);
    //console.log(data.fileName);
    if ($.trim($("#grid-container").html()) == "") {
    $("#main-grid-container").html('<div class="row grid grid-margin" id="grid-container">' + data.html + '</div>');
    } else {
    var Items = parseInt($("#totalMediaItems").html());
    Items += 1;
    $("#totalMediaItems").html(Items);
    $("#grid-container").prepend(data.html);

    $(".item-orders").each(function (index, element) {
    $(this).html(index + 1)
    });

    }
    $("[data-file-name='" + data.fileName + "']").remove();
    if ($("[data-file-name]").length == 0)
    {
    $('#progress .ajax-file-upload-progress').css(
    'width',
    '0%'
    ).html('');
    }
    })
    .error(function (jqXHR, textStatus, errorThrown) {/* ... */})
    .complete(function (result, textStatus, jqXHR) {/*....*/});
    },
    start: function (e) {
    $(".ajax-file-upload-container").removeClass('hide');
    },
    progressall: function (e, data) {
    var progress = parseInt(data.loaded / data.total * 100, 10);
    $('#progress .ajax-file-upload-progress').css(
    'width',
    progress + '%'
    ).html(progress + '%');
    }
    });
    //Helper function for calculation of progress
    function formatFileSize(bytes) {
    if (typeof bytes !== 'number') {
    return '';
    }

    if (bytes >= 1000000000) {
    return (bytes / 1000000000).toFixed(2) + ' GB';
    }

    if (bytes >= 1000000) {
    return (bytes / 1000000).toFixed(2) + ' MB';
    }
    return (bytes / 1000).toFixed(2) + ' KB';
    }
    })