Search code examples
javascriptformsmultifile-uploader

Can not preview multiple uploading files in form


In a multipart form, I can preview a single uploading image using:

 function readURL(input) {
      if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
          let image = new Image();
          image.src = `${e.target.result}`;
          image.className = "img-thumbnail"
          let closeBtn = `<button type="button" class="close"></button>`;
          let wrapper = $('<div class="image-wrapper" />');
          $('#images-to-upload').append(wrapper);
          $(wrapper).append(image);
          $(wrapper).append(closeBtn);
        }
    
        reader.readAsDataURL(input.files[0]); 
      }
    }
    
    $("#imgInput").change(function () {
      readURL(this);
    });

But I'd like to preview ALL uploading images, so I made this adjustment to the code above by adding a for loop:

function readURL(input) {
  if (input.files && input.files[0]) {
    let files = input.files;
    
    for (var i = 0; i < input.files.length; i++) {
      var reader = new FileReader();
      reader.onload = function (e) {
        let image = new Image();
        image.src = `${e.target.result[i]}`;
        image.className = "img-thumbnail"
        let closeBtn = `<button type="button" class="close"></button>`;
        let wrapper = $('<div class="image-wrapper" />');
        $('#images-to-upload').append(wrapper);
        $(wrapper).append(image);
        $(wrapper).append(closeBtn);
  
      }
      };

    reader.readAsDataURL(input.files[i]); // error here
  }
}

But now I get this error:

143 Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'

How can I fix this?


Solution

  • It's because the reader.readAsDataURL(input.files[i]); is outside the loop. But this is not how you do this. The FileReader can process only one file at the time. This means you have to create an instance of the FileReader for each image in the input.

    I would suggest to split it into 2 functions for readability.

    
    function previewImage(file) {
      const reader = new FileReader();
      reader.onload = function (e) {
        let image = new Image();
        image.src = e.target.result;
        image.className = "img-thumbnail";
        let closeBtn = `<button type="button" class="close"></button>`;
        let wrapper = $('<div class="image-wrapper" />');
        $("#images-to-upload").append(wrapper);
        $(wrapper).append(image);
        $(wrapper).append(closeBtn);
      };
      reader.readAsDataURL(file);
    }
    
    function readURL(input) {
      if (input.files.length) {
        Array.from(input.files).forEach((file) => previewImage(file));
      }
    }
    

    I made some changes:

    • if (input.files.length) { - the file input always have files FileList object so no need to check if it exists, and you just check if it has a length, meaning at least one file is present
    • Array.from(input.files) - transforms FileList into a regular array fo you can use array functions, like forEach

    The rest is pretty much the same. In image.src = e.target.result;, there's no need to make it string as it is already a string. Also the result set on the FileReader class cannot be array.