Search code examples
javascriptjqueryblueimp

blueimp File Upload fires add event multiple times


I'm trying to build upload module that will be used in my website project. I've selected blueimp File Upload because of all configuration options that it gives.

Idea is to have button, that will show modal window with upload module.
My (almost) working prototype is available here: http://jsfiddle.net/Misiu/4Th3u/

What I want now is to limit number of files user can select and file size. Because I'm using non-ui version I can't use maxNumberOfFiles and maxFileSize options.

I've created add callback:

add: function (e, data) {
    var uploadErrors = [];

    console.log('add event');      
    $.each(data.originalFiles, function(index,file) {
        console.log(file.name);

        if (file.size && file.size > 1024 * 1024 * 5) {
            uploadErrors.push('File "' + file.name + '" is too large');
        }
    })

    if (uploadErrors.length > 0) {
        alert(uploadErrors.join("\n"));
    } else {
        var tpl = $('<li class="working"><input type="text" value="0" data-width="36" data-height="36"' +' data-fgColor="#0788a5" data-readOnly="1" data-bgColor="#3e4043" /><p></p><span></span></li>');
        tpl.find('p').text(data.files[0].name)
            .append('<i>' + formatFileSize(data.files[0].size) + '</i>');
        data.context = tpl.appendTo(ul);
        tpl.find('input').knob();
        tpl.find('span').click(function () {
        if (tpl.hasClass('working')) {
            jqXHR.abort();
        }
        tpl.fadeOut(function () {
            tpl.remove();
        });
    });
    var jqXHR = data.submit();
    }
}

Problem is that add is fired multiple times, if I select 2 files I get 2 events.
Here is how console looks after selecting two files:

add event
file1.gif
file2.gif
add event
file1.gif
file2.gif 

I would like to limit number of files and file size, but because of this bug it's not easy.


Solution

  • I can't answer your specific question but I've had to overcome the issue of validating selected files before upload. You can use the maxFileSize properties in the non-ui version, you just need to surface any errors to the UI yourself. You also need to ensure that the process and validate JS files are also referenced on the page.

    Here's my solution which unfortunately has the progress stuff stripped out but the image preview left in! It shouldn't be too hard for you to hack the template stuff to suit your needs though.

    My form looks like this:

    <form id="FileUpload" action="/Expense/UploadReceipt" method="POST" enctype="multipart/form-data">
        <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
        <div class="row fileupload-buttonbar">
            <div class="col-md-12">
    
                <input type="file" name="files[]" multiple class="btn btn-default">
    
                <button type="reset" class="btn btn-danger cancel">
                    <i class="glyphicon glyphicon-ban-circle"></i>
                    <span>Cancel All</span>
                </button>
                <button type="submit" class="btn btn-success start">
                    <i class="glyphicon glyphicon-upload"></i>
                    <span>Start upload</span>
                </button>
            </div>
        </div>
        <!-- The loading indicator is shown during image processing -->
        <div class="fileupload-loading"></div>
        <br>
        <!-- The table listing the files available for upload/download -->
        <table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody></table>
    </form>
    

    My File upload initialisation looks like this:

    $('#FileUpload').fileupload({
            // Uncomment the following to send cross-domain cookies:
            //xhrFields: {withCredentials: true},
            url: uploadUrl + data,
            dataType: 'json',
            headers: {
                Accept: "application/json"
            },
            accept: 'application/json',
            maxFileSize: 5000000, //5mb
            sequentialUploads: true,
            resizeMaxWidth: 1920,
            resizeMaxHeight: 1200,
            acceptFileTypes: /(.|\/)(gif|jpe?g|png|pdf)$/i,
            uploadTemplateId: null,
            downloadTemplateId: null,
            uploadTemplate: function (o) {
                var rows = $();
                $.each(o.files, function (index, file) {
                    var row = $('<tr class="template-upload fade">' +
                        '<td class="preview"><span class="fade"></span></td>' +
                        '<td class="name"><strong class="error text-danger"></strong></td>' +
                        '<td class="size"></td>' +
                        (file.error ? '<td class="error" colspan="1"></td>' :
                                '<td class="actions-col">' +
                                '<button class="btn btn-danger cancel"><i class="glyphicon glyphicon-ban-circle"></i> <span>Cancel</span></button> ' +
                                '<button class="btn btn-success start"><i class="glyphicon glyphicon-upload"></i> <span>Start</span></button>' +
                                ' </td>') + '</tr>');
                    row.find('.name').text(file.name);
                    row.find('.size').text(o.formatFileSize(file.size));
                    if (file.error) {
                        row.find('.error').text(
                            locale.fileupload.errors[file.error] || file.error
                        );
                    }
                    rows = rows.add(row);
                });
                return rows;
            },
            downloadTemplate: function (o) {
                var rows = $();
                $.each(o.files, function (index, file) {
    
                    var row = $('<tr class="template-download fade">' +
                        (file.error ? '<td></td><td class="name"></td>' +
                            '<td class="size"></td><td class="error" colspan="2"></td>' :
                                '<td class="preview"></td>' +
                                    '<td class="name"><a></a></td>' +
                                    '<td class="size"></td><td colspan="2"></td>'
                        ));
                    row.find('.size').text(o.formatFileSize(file.size));
                    if (file.error) {
                        //row.find('.name').text(file.name);
                        //row.find('.error').text(
                        //    locale.fileupload.errors[file.error] || file.error
                        //);
                    } else {
                        row.find('.name a').text(file.name);
                        var extension = file.name.substring(file.name.length - 3, file.name.length);
                        if (extension == "pdf") {
                            row.find('.name a').attr('target', '_blank');
                        } else {
                            row.find('.name a').addClass("fancyImageLink");
                        }
                        if (file.thumbnail_url) {
                            row.find('.preview').append('<a><img></a>')
                                .find('img').prop('src', file.thumbnail_url);
                            row.find('a').prop('rel', 'gallery');
                        }
                        row.find('a').prop('href', file.url);
                        row.find('.delete')
                            .attr('data-type', file.delete_type)
                            .attr('data-url', file.delete_url);
                    }
                    rows = rows.add(row);
                });
                return rows;
            }
        });
    

    The error handling is done here:

    $('#FileUpload').bind('fileuploadprocessalways', function (e, data) {
                var currentFile = data.files[data.index];
                if (data.files.error && currentFile.error) {
                    $('.files tr').eq(data.index).find(".start").prop('disabled', true);
                    if (currentFile.error == "File is too large") {
                        $('.files tr').eq(data.index).find(".size").addClass('field-validation-error');
                    } else {
                        $('.files tr').eq(data.index).find(".name").addClass('field-validation-error');
                    }
    
                    $("#ReceiptUploadAlert p").text(currentFile.name + ": " + currentFile.error);
                    $("#ReceiptUploadAlert").show();
                    return;
                }
            });
    

    Hope this helps you in some way.