Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uncaught DOMException: Failed to execute 'readAsDataURL' on 'FileReader': The object is already busy reading Blobs.(…)

Tags:

javascript

I am working on this for the past 6 hours, and I am unable to figure this out.

I am trying to create a website, in which I choose a folder of images and I show them in my document. I get the first image to show, and then I get the following error in my console.

Uncaught DOMException: Failed to execute 'readAsDataURL' on 'FileReader': The object is already busy reading Blobs.(…)

I believe the issue is caused due to my for loop because the filereader is asynchronous. But I need to loop through the whole array for this, so what am I doing wrong?

I load the files ("will check to make sure I get only images later"), into an array, and then I read each file one at a time. Before breaking down my code to functions I did everything in a single function and it works! I included the HTML + the original and current JS code. Thank you for taking the time to see this.

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Tiled Image Viewer</title>
    <script src="js/tiv.js"></script>
    <link rel="stylesheet" href="style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

  </head>
  <body>

    <div id="main-wrap">
      <form name="uploadForm">

        <input id="images" type="file" webkitdirectory mozdirectory directory name="myFiles"
                   onchange="readAndShowFiles();" multiple/>

        <span id="list"></span>
      </form>

    </div>

  </body>
</html>

Javascript Original:

function readAndShowFiles() {
 var files = document.getElementById("images").files;
 for (var i = 0; i < files.length; i++) {
   var file = files[i];
   // Have to check that this is an image though
   // using the file.name TODO
   var reader = new FileReader();
   // Closure to capture the file information.
   reader.onload = (function(file) {
     return function(e) {
       // Render thumbnail.
       var span = document.createElement('span');
       span.innerHTML = ['<img src="', e.target.result,
       '" title="', escape(file.name), '">'].join('');
       document.getElementById('list').insertBefore(span, null);
     };
   })(file);
   // Read in the image file as a data URL.
   reader.readAsDataURL(file);
 }
}

Javascript Current:

function readAndShowFiles() {
    console.log("Checkpoint2"); //test
    var tiv = new tivAPI();
    var array = tiv.getLoadedImages();
    tiv.showLoadedImages(array);
}

function tivAPI(){
  var imagesarray = new Array();


    return{
      loadImages: function(){
        console.log("Loading Files"); //test
        var files = document.getElementById("images").files;
        for (var i = 0; i < files.length; i++) {
          var file = files[i];
          // Have to check that this is an image though
          // using the file.name TODO
        }
        console.log(files.length); //test
        return files;
      },
      getLoadedImages: function(){
        imagesarray = this.loadImages();
        console.log("Returning Files"); //test
        console.log(imagesarray.length);
        return imagesarray;
      },
      showLoadedImages: function(elem){
        console.log("Showing Files"); //test
        var files = elem;
        var reader = new FileReader();
        // Closure to capture the file information.
        for (var i = 0; i < files.length; i++) {
          var file = files[i];
          reader.onload = (function(file) {
            return function(e) {
              // Render thumbnail.
              var span = document.createElement('span');
               span.innerHTML = ['<img src="', e.target.result,
              '" title="', escape(file.name), '">'].join('');
              document.getElementById('list').insertBefore(span, null);
            };

          })(file);
        // Read in the image file as a data URL.
        reader.readAsDataURL(file);
        }
      }
    };
  }
like image 508
Stavros Argyrou Avatar asked Oct 31 '16 18:10

Stavros Argyrou


2 Answers

The reason why your code fails is that you are using the same reader variable on each subsequent loop iteration.

When Javascript parses your code, what happens is that all variables get moved to the top of the function, so your parsed code looks something like this:

    function readAndShowFiles() {
      var files = document.getElementById("images").files;
      var reader;
      var file;
      var i;

      for (i = 0; i < files.length; i++) {
        file = files[i];
        reader = new FileReader();
        reader.onload = (function(file) {
          return function(e) {
            var span = document.createElement('span');
            span.innerHTML = ['<img src="', e.target.result,
              '" title="', escape(file.name), '">'
            ].join('');
            document.getElementById('list').insertBefore(span, null);
          };
        })(file);
        reader.readAsDataURL(file);
      }
    }

You could avoid this error quite simply by just moving all the logic inside the anonymous function like so:

    function readAndShowFiles() {
      var files = document.getElementById("images").files;

      for (var i = 0; i < files.length; i++) {
        // Closure to capture the file information.
        (function(file) {
          var reader = new FileReader();
          reader.onload = function(e) {
            // Render thumbnail.
            var span = document.createElement('span');
            span.innerHTML = ['<img src="', e.target.result,
              '" title="', escape(file.name), '">'
            ].join('');
            document.getElementById('list').insertBefore(span, null);
          };
          // Read in the image file as a data URL.
          reader.readAsDataURL(file);
        })(files[i]);
      }
    }

Now you are using a unique reader variable for each file iteration. Note also that this could be simpler if you just did:

    array.from(files).forEach(function(file) {
      // code
    })

Or just used the let variable (that way you will use a different FileReader every time):

    function readAndShowFiles() {
      var files = document.getElementById("images").files;

      for (let i = 0; i < files.length; i++) {
        let file = files[i];
        let reader = new FileReader();
        reader.onload = function(e) {
          // Render thumbnail.
          var span = document.createElement('span');
          span.innerHTML = ['<img src="', e.target.result,
            '" title="', escape(file.name), '">'
          ].join('');
          document.getElementById('list').insertBefore(span, null);
        };
        // Read in the image file as a data URL.
        reader.readAsDataURL(file);
      }
    }

The for loop could be written easier with es6 leaving you with 2 less variables

    function readAndShowFiles() {
      var files = document.getElementById("images").files;

      for (let file of files) {
        let reader = new FileReader();
        reader.onload = function(e) {
          // Render thumbnail.
          var span = document.createElement('span');
          span.innerHTML = ['<img src="', e.target.result,
            '" title="', escape(file.name), '">'
          ].join('');
          document.getElementById('list').insertBefore(span, null);
        };
        // Read in the image file as a data URL.
        reader.readAsDataURL(file);
      }
    }

But if you want it super easy, why not just skip the FileReader altogether and avoid all functions & callbacks with URL.createObjectURL? Using createObjectURL you save CPU de/compiling the file to/from base64:

    function showFiles() {
      var files = document.getElementById("images").files;

      for (let file of files) {
        let img = new Image;
        img.src = URL.createObjectURL(file);
        img.title = file.name;

        document.getElementById('list').appendChild(img);
      }
    }
like image 85
Endless Avatar answered Nov 16 '22 10:11

Endless


the Problem occured because I was trying to use the same reader for every image. Moving var reader = new FileReader();into the loop (ShowLoadedImages function), solved the issue and showed all the images like it was supposed to.

like image 28
Stavros Argyrou Avatar answered Nov 16 '22 10:11

Stavros Argyrou