0

I'm trying to make a file uploader for my blog system which would just let users drop files in it and it would automatically upload them to server. Strangely (for me), console.log outputs dataArray before it gets filled, while calling it after a timeout outputs it correctly.

For example, if I drop 4 files on my drop area, I would get this:

[]
[file1, file2, file3, file4]

Then I drop 4 more files without uploading/refreshing and I get:

[file1, file2, file3, file4]
[file1, file2, file3, file4, file5, file6, file7, file8]

So my script is working asynchronously for some reason? Can somebody tell me what I'm doing wrong here?

var dataArray    = [];

$('.dropArea').bind(
{
    drop: function(e)
    {
        e.stopPropagation();
        e.preventDefault();
        var files = e.dataTransfer.files;

        $.each(files, function(index, file)
        {
            var fileReader = new FileReader();

            fileReader.onload = (function(file)
            {
                return function(e)
                {
                    var image = this.result;

                    dataArray.push({
                        name : file.name,
                        value: image
                    });
                }
            })(files[index]);

            fileReader.readAsDataURL(file);
        });

                    console.log(dataArray);
        setTimeout(function() { console.log(dataArray) }, 1000);
    },
});

2 Answers 2

1

you should console.log() in the callback.

fileReader.onload = (function(file)
{
    return function(e)
    {
        var image = this.result;

        dataArray.push({
            name : file.name,
            value: image
        });

        console.log(dataArray);
    }
})(files[index]);

if you call it outside of the callback, it will run immediately after the image is starting to load instead of when the images are finished loading.

I've quickly drawn an image for clarification:

description

You can solve this by comparing the amount of images that is dropped and the amount of images that finished loading, like so:

var dataArray    = [];
var count = 0; // amount of files dropped
var ready = 0; // amount of files finished loading

$('.dropArea').bind(
{
    drop: function(e)
    {
        ...

        $.each(files, function(index, file)
        {
            count++; // we start handling a file here so we increment

            ...

            fileReader.onload = (function(file)
            {
                return function(e)
                {
                    ...

                    ready++; // this image has finished loading so we increment
                }
            })(files[index]);
        });

        setTimeout(function() {
            if(ready === count) {
                // all images have been loaded
                console.log(dataArray);
            }
        }, 1000);
    },
});
Sign up to request clarification or add additional context in comments.

1 Comment

Was just about to ask if there is an event for when file is loaded, but you were faster. It's a pity that there is no way to bulk-queue files for loading and make it fire event when everything is done :( . Thanks a lot for your response and effort !
0

Just as @Tim S. answered, onload event is fired when file starts loading, and it can take some time depending on file size. Here is how I solved discovering if all files are loaded.

drop: function(e)
    {
        e.stopPropagation();
        e.preventDefault();
        var files = e.dataTransfer.files;
        console.log(files.length + " queued for upload");

        var counter = 0;
        var loaded = 0;
        $.each(files, function(index, file)
        {
            counter++;
            console.log("File #"+counter+" loading started.")
            if (!files[index].type.match('image.*'))
            {
                $('.dropArea').html(message.error.onlyImage);
                errorMessage++;
            }
            var fileReader = new FileReader();

            fileReader.onload = (function(file, count)
            {
                return function(e)
                {
                    var image = this.result;

                    dataArray.push({
                        name : file.name,
                        value: image
                    });

                    loaded++;
                    console.log("File #"+count+" loaded.");

                    if (loaded == files.length) {
                        console.log("Loading finished!");
                    }
                }
            })(files[index], counter);

            fileReader.readAsDataURL(file);
        });
    }

And really, console log output looks like this:

4 queued for upload 9a840a0_part_2_dragndrop_2.js:25
File #1 loading started. 9a840a0_part_2_dragndrop_2.js:32
File #2 loading started. 9a840a0_part_2_dragndrop_2.js:32
File #3 loading started. 9a840a0_part_2_dragndrop_2.js:32
File #4 loading started. 9a840a0_part_2_dragndrop_2.js:32
File #2 loaded. 9a840a0_part_2_dragndrop_2.js:52
File #4 loaded. 9a840a0_part_2_dragndrop_2.js:52
File #3 loaded. 9a840a0_part_2_dragndrop_2.js:52
File #1 loaded. 9a840a0_part_2_dragndrop_2.js:52
Loading finished! 

As can be seen, file #2 was loaded first because it is the smallest one, while #1 loaded last while it is the largest.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.