Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Progress bar while uploading large files with XMLHttpRequest

I am trying to upload some large files to the server using XMLHttpRequest and file.slice.
I've manage doing this with the help of documentations and other various links.
Since uploading large file is a lengthily job, i would like to provide the user with a progress bar.
After some more readings i've come across on an example that, theoretically, does exactly what i need.
By taking the sample code and adapting it to my needs i reached

var upload =
{
blobs: [],
pageName: '',
bytesPerChunk: 20 * 1024 * 1024,
currentChunk: 0,
loaded: 0,
total: 0,
file: null,
fileName: "",

uploadChunk: function (blob, fileName, fileType) {
    var xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            if (xhr.responseText) {
                // alert(xhr.responseText);
            }
        }
    };

    xhr.addEventListener("load", function (evt) {
        $("#dvProgressPrcent").html("100%");
        $get('dvProgress').style.width = '100%';
    }, false);

    xhr.addEventListener("progress", function (evt) {
        if (evt.lengthComputable) {
            var progress = Math.ceil(((upload.loaded + evt.loaded) / upload.total) * 100);
            $("#dvProgressPrcent").html(progress + "%");
            $get('dvProgress').style.width = progress + '%';
        }
    }, false);

    xhr.upload.addEventListener("progress", function (evt) {
        if (evt.lengthComputable) {
            var progress = Math.ceil(((upload.loaded + evt.loaded) / upload.total) * 100);
            $("#dvProgressPrcent").html(progress + "%");
            $get('dvProgress').style.width = progress + '%';
        }
    }, false);

    xhr.open('POST', upload.pageName, false);

    xhr.setRequestHeader("Content-Type", "multipart/form-data");
    xhr.setRequestHeader("X-File-Name", fileName);
    xhr.setRequestHeader("X-File-Type", fileType);
    xhr.send(blob);
},
upload: function (file) {
    var start = 0;
    var end = 0;
    var size = file.size;

    var date = new Date();
    upload.fileName = date.format("dd.MM.yyyy_HH.mm.ss") + "_" + file.name;

    upload.loaded = 0;
    upload.total = file.size;

    while (start < size) {
        end = start + upload.bytesPerChunk;
        if (end > size) {
            end = size;
        }

        var blob = file.slice(start, end);
        upload.uploadChunk(blob, upload.fileName, file.type);
        start = end;
        upload.loaded += start;
    }

    return upload.fileName;
}
};

The call is like (without the validations)

upload.upload(document.getElementById("#upload").files[0]);

My problem is that the progress event doesn't trigger.
I've tried xhr.addEventListener and with xhr.upload.addEventListener (each at a time and both at a time) for the progress event but it never triggers. The onreadystatechange and load events trigger just fine.

I would greatly appreciate help with what i am doing wrong

Update
After many attempts i've manage to simulate a progress but i've ran into another problem: Chrome's UI is not updating during the upload.
The code looks like this now

var upload =
{
    pageName: '',
    bytesPerChunk: 20 * 1024 * 1024,
    loaded: 0,
    total: 0,
    file: null,
    fileName: "",

    uploadFile: function () {
        var size = upload.file.size;

        if (upload.loaded > size) return;

        var end = upload.loaded + upload.bytesPerChunk;
        if (end > size) { end = size; }

        var blob = upload.file.slice(upload.loaded, end);

        var xhr = new XMLHttpRequest();

        xhr.open('POST', upload.pageName, false);

        xhr.setRequestHeader("Content-Type", "multipart/form-data");
        xhr.setRequestHeader("X-File-Name", upload.fileName);
        xhr.setRequestHeader("X-File-Type", upload.file.type);

        xhr.send(blob);

        upload.loaded += upload.bytesPerChunk;

        setTimeout(upload.updateProgress, 100);
        setTimeout(upload.uploadFile, 100);
    },
    upload: function (file) {
        upload.file = file;

        var date = new Date();
        upload.fileName = date.format("dd.MM.yyyy_HH.mm.ss") + "_" + file.name;

        upload.loaded = 0;
        upload.total = file.size;

        setTimeout(upload.uploadFile, 100);


        return upload.fileName;
    },
    updateProgress: function () {
        var progress = Math.ceil(((upload.loaded) / upload.total) * 100);
        if (progress > 100) progress = 100;

        $("#dvProgressPrcent").html(progress + "%");
        $get('dvProgress').style.width = progress + '%';
    }
};


Update 2
I've managed to fix it and simulate a progress bar that works in chrome too.
i've updated previous code sample with the one that works.
You can make the bar 'refresh' more often by reducing the size of the chunk uploaded at a time Tahnk you for your help

like image 442
Cioby Avatar asked Apr 12 '13 07:04

Cioby


People also ask

How do I upload a file using Xmlhttprequest?

You have to use the FormData object to wrap the file into a multipart/form-data post data object: var formData = new FormData(); formData. append("thefile", file); xhr. send(formData);


2 Answers

As stated in https://stackoverflow.com/a/3694435/460368, you could do :

if(xhr.upload)
     xhr.upload.onprogress=upload.updateProgress;

and

updateProgress: function updateProgress(evt) 
{
   if (evt.lengthComputable) {
       var progress = Math.ceil(((upload.loaded + evt.loaded) / upload.total) * 100);
       $("#dvProgressPrcent").html(progress + "%");
       $get('dvProgress').style.width = progress + '%';
   }
}
like image 180
Shikiryu Avatar answered Sep 27 '22 20:09

Shikiryu


There is my solution:

function addImages(id) {
  var files = $("#files").prop("files");
  var file = files[loopGallery];
  var cList = files.length;

  var fd = new FormData();
  fd.append("file", file);
  fd.append("galerie", id);

  var xhr = new XMLHttpRequest();
  xhr.open("POST", "moduls/galerie/uploadimages.php", true);
  xhr.upload.onprogress = function(e) {
    var percentComplete = Math.ceil((e.loaded / e.total) * 100);
    $("#progress").css("display","");
    $("#progressText").text((loopGallery+1)+" z "+cList);
    $("#progressBar").css("width",percentComplete+"%");
  };

  xhr.onload = function() {
    if(this.status == 200) {
      $("#progressObsah").load("moduls/galerie/showimages.php?ids="+id);
      if((loopGallery+1) == cList) {
        loopGallery = 0;
      } else {
        $("#progressBar").css("width", "0%");  
        loopGallery++;
        addImages(id);
      }
    }
  }

  if(cList > 0) {
    xhr.send(fd);
  }
}
like image 37
koca79331 Avatar answered Sep 27 '22 19:09

koca79331