1

This input element allows to take multiple images, for example if i select 2 images i get: FileList {0: File, 1: File, length: 2}. Then if i select again for example 1 image i get another FileList: FileList {0: File, length: 1}. There's a chance to push this new image to the first FileList? And it's possible to remove an image of the FileList by his index? Thanks in advance.

function handleFileSelect(evt) {
      var files = evt.target.files; // FileList object
      console.log(files);

      for (var i = 0; i < files.length; i++) {          
        console.log(files[i]);
      }
      // Loop through the FileList and render image files as thumbnails.
      for (var i = 0, f; f = files[i]; i++) {
        if (!f.type.match('image.*')) {
          continue;
        }

      let reader = new FileReader();
      reader.onload = (function(theFile) {
        return function(e) {
          // Render thumbnail.
          var span = document.createElement('span');
          span.innerHTML = ['<img class="thumb" src="', e.target.result,
                            '" title="', escape(theFile.name), '"/>'].join('');
          document.getElementById('list').insertBefore(span, null);
        };
      })(f);

      reader.readAsDataURL(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
.thumb {
      height: 75px;
      border: 1px solid #000;
      margin: 10px 5px 0 0;
    }
<input type="file" id="files" name="files[]" multiple />
  <output id="list"></output>

enter image description here

sonEtLumiere
  • 4,461
  • 3
  • 8
  • 35
  • It sounds like you're claiming that if you `console.log(files.length)`, that it will always output `1`? I can't reproduce that. If I select multiple files, then `files.length` is the number of files that I selected. – Wyck Dec 19 '20 at 01:56
  • 2
    FileList isn't always 1. I've uploaded 3 items and it's showing 3. – Sifat Haque Dec 19 '20 at 01:58
  • i uploaded an image how i see FileList in console – sonEtLumiere Dec 19 '20 at 02:39

2 Answers2

2

You are selecting only a single file 3 times.

To get multiple files you need to select them in a single call from the file-picker.
It will depend on the OS, but on my macOS I can do so by pressing the cmd key (probably ctrl on other OSes) while clicking on the items I want to select.

screenshot of the selectpicker with multiple files screenshot of the js console result

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Nice! it's working. Is there any chance to get a FileList by selecting files individually ? Thanks – sonEtLumiere Dec 19 '20 at 05:27
  • @sonEtLumiere you are always getting a FileList, even if it contains a single file. There are [ways](https://stackoverflow.com/questions/47119426/how-to-set-file-objects-and-length-property-at-filelist-object-where-the-files-a) to append files to the `input.files`, but wouldn't an Array just be enough for your case? – Kaiido Dec 19 '20 at 05:51
  • Then an Array and a FormData is the way to go. – Kaiido Dec 19 '20 at 07:30
1

This is the way you can achieve your desired result using FormData.

You can change the code to achieve your desired look and functionality.

In summary what the following code does:

  1. Creates a new FormData instance
  2. Accepts files from file input
  3. Appends the file to a field named file-${index} field of FormData created in step 1
  4. Creates a FileReader instance for each File
  5. The FileReader reads the file content and returns data: URL representing the file's data as a base64 encoded string.
  6. Creates a span element and appends an img element to it with the fileContent of step 5 as src
  7. Attaches the click listener to the span so when the user clicks an image the corresponding file will be removed from FormData of step 1 and the span will be removed from DOM
  8. The FormData will be sent to the backend as a request body using Fetch API when the user clicks the submit files button using the sendFiles function as click handler.

you can see the list of files attached to FormData with their corrponding form field name and original filename under sunbmit button as a ul generated using listFiles function

const formData = new FormData();

// to be used for files field names of FormData
let index = 0;

// for listing current files
const listFiles = () => {
  const list = document.createElement("ul");
  Array.from(formData.entries()).forEach((entry) => {
    const item = document.createElement("li");
    item.innerHTML = entry[0] + ": " + entry[1].name;
    list.appendChild(item);
  });
  document.querySelector("#fileList").innerHTML = list.innerHTML;
};

// for sending files to the backend
const sendFiles = () => {
  fetch("/upload/path", {
    body: formData,
    method: "POST",
  })
    .then((response) => response.json())  // If the response is in JSON format
    .then((data) => {
      console.log(data);
    })
    .catch((error) => {
      console.error(error);
    });
};

// for outputting fileReader output and file for FormData
// it needs to be async because of async nature of fileReader onload event so we can keep FormData and FileReader in sync using index
const readFile = (file) => {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.onload = (event) => {
      const theFile = event.target;
      return resolve([file, theFile]);
    };
    fileReader.readAsDataURL(file);
  });
};

const handleFileSelect = async (event) => {
  var files = event.target.files;
  for (const file of files) {
    if (file.type.match("image.*")) {
      const [fileData, theFile] = await readFile(file);
      const id = `file-${index}`;
      formData.append(id, fileData);
      const span = document.createElement("span");
      const img = document.createElement("img");
      img.src = theFile.result;
      img.alt = "Image thumb";
      img.title = escape(fileData.name);
      img.className = "thumb";
      span.appendChild(img);
      // for removing the thumbnail and its linked file from FormData
      span.addEventListener("click", () => {
        formData.delete(id);
        // listing current files appended to FormData after removing this thumbnail
        listFiles();
        span.remove();
      });
      index++;
      document.getElementById("list").insertBefore(span, null);
    }
  }
  // list files after  adding new file/files
  listFiles();
};

document.getElementById("files").addEventListener("change", handleFileSelect, false);
.thumb {
      height: 75px;
      border: 1px solid #000;
      margin: 10px 5px 0 0;
    }
<input type="file" id="files" name="files[]" multiple />
<br />
<h3>click on images to remove them</h3>
<output id="list"></output>
<br /><br /><br />
<button onclick="sendFiles()">submit Files</button>
<ul id="fileList"></ul>

example of How to upload a file using Fetch for refrence.

Abbas Hosseini
  • 1,545
  • 8
  • 21