51

How to remove one specific selected file from input file control?

I have an input file control with the option to select multiple files; however, I want to validate a file and if it has an wrong extension then I should remove that file from the file control itself, is it possible?

I tried as below

<input type="file" name="fileToUpload" id="fileToUpload" multiple/>


<script> $("#fileToUpload")[0].files[0] </script>

Below is the screenshot of the object but I am not able to modify it

enter image description here

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Navin Leon
  • 1,136
  • 3
  • 22
  • 44
  • what are you going to do with the files? once the user uploads them they are just copied to your tmp folder and their info is held in the array you have, so when you go to process them just skip whatever ones have the wrong extensions... – A.O. Sep 27 '13 at 21:35
  • 4
    Guys, its not an array, its an object with attribute "0","1","2" so I cannot do array operations here – Navin Leon Sep 27 '13 at 21:48
  • 1
    Unfortunately, you cannot modify the files in a FileList, as they are read only. It is also seemingly impossible to create a new FileList. Any chance your problem could be solved using the 'accepts' attribute of the input element? You can remove ALL files from the FileList with this: `$("#fileToUpload")[0].value = ''` – mako-taco Oct 07 '13 at 21:44
  • Related post: [How to set a value to a file input in HTML?](https://stackoverflow.com/q/1696877/2873538) – Ajeet Shah May 14 '20 at 14:44

5 Answers5

44

As other people pointed out, FileList is read only. You can get around this by pushing those files into a separate Array though. You can then do whatever you want with that curated list of files. If uploading them to a server is the goal, you can use the FileReader API.

Below is a round about way of completely avoiding needing to modify the FileList. Steps:

  1. Add normal file input change event listener
  2. Loop through each file from change event, filter for desired validation
  3. Push valid files into separate array
  4. Use FileReader API to read files locally
  5. Submit valid, processed files to server

Event handler and basic file loop code:

var validatedFiles = [];
$("#fileToUpload").on("change", function (event) {
  var files = event.originalEvent.target.files;
  files.forEach(function (file) {
    if (file.name.matches(/something.txt/)) {
      validatedFiles.push(file); // Simplest case
    } else { 
      /* do something else */
    }
  });
});

Below is a more complicated version of the file loop that shows how you can use the FileReader API to load the file into the browser and optionally submit it to a server as a Base64 encoded blob.

  files.forEach(function (file) {
    if (file.name.matches(/something.txt/)) { // You could also do more complicated validation after processing the file client side
      var reader = new FileReader();
      // Setup listener
      reader.onload = (function (processedFile) {
        return function (e) {
          var fileData = { name : processedFile.name, fileData : e.target.result };

          // Submit individual file to server
          $.post("/your/url/here", fileData);

          // or add to list to submit as group later
          validatedFiles.push(fileData);
        };
      })(file);

      // Process file
      reader.readAsDataURL(file);
    } else { 
      /* still do something else */
    }
  });

A note of caution about using FileReader API. Base64 encoding a file will increase its size by around 30%. If that isn't acceptable you will need to try something else.

Andrew Hubbs
  • 9,338
  • 9
  • 48
  • 71
  • 2
    Hi, just one thing. files is an javascript object so you can't use .forEach on it, you should do this instead [].forEach.call(files, function(file){....}) – Fibonacci Oct 14 '14 at 13:16
  • This solution only works if you're NOT uploading the files from Form Submit – Jorge Oct 31 '15 at 22:29
  • instead of submitting individual files to the server, how can I get it to post the whole array? – Alex Nov 05 '15 at 10:12
  • @AndrewHubbs how to validated file attach with multiple file input again then ? – kez Feb 26 '17 at 06:02
  • Where do you remove the files from the input element? If a user uploads 5 files and all you're doing is manipulating a JavaScript array, they should still see '5 files selected' next to the input button. – default123 Sep 10 '20 at 00:12
6

I know this is an old post but I have spent ages trying to work around this one so I'll post my solution. There is a way to update the fileList of an fileField element with another fileList - which can be done with DataTransfer:

let updateFileList = function (fileField, index) {
  let fileBuffer = Array.from(fileField.files);
  fileBuffer.splice(index, 1);

  /** Code from: https://stackoverflow.com/a/47172409/8145428 */
  const dT = new ClipboardEvent('').clipboardData || // Firefox < 62 workaround exploiting https://bugzilla.mozilla.org/show_bug.cgi?id=1422655
    new DataTransfer(); // specs compliant (as of March 2018 only Chrome)

  for (let file of fileBuffer) { dT.items.add(file); }
  fileField.files = dT.files;
}

The above function takes the fileField as a DOM Object and the index of the file in the fileField's fileList to be removed and updates the passed fileField accordingly.

Hope this saves somebody else some time!

How to set File objects and length property at FileList object where the files are also reflected at FormData object?

liamthorne4
  • 315
  • 2
  • 11
5

I thought that I should add my comment here as well here (I've answered here: JavaScript delete File from FileList to be uploaded)

I found a workaround. This will not require AJAX for the request at all and the form can be sent to the server. Basically you could create an hidden or text input and set it's value attribute to the base64 string created after processing the file selected.

<input type=hidden value=${base64string} />

You will probably consider the idea to create multiple input file instead of input text or hidden. This will not work as we can't assign a value to it.

This method will include the input file in the data sent to the database and to ignore the input file you could:

  • in the back-end don't consider the field;
  • you can set the disabled attribute to the input file before serialising the form;
  • remove the DOM element before sending data.

When you want to delete a file just get the index of the element and remove the input element (text or hidden) from the DOM.

Requirements:

  • You need to write the logic to convert files in base64 and store all files inside an array whenever the input file trigger the change event.

Pros:

  • This will basically give you a lot of control and you can filter, comparing files, check for file size, MIME type, and so on..
Michael Mammoliti
  • 1,517
  • 13
  • 11
3

Based on @liamthorne4 answer, here is a working solution for upload, list and delete element from list, tested on Firefox and Chrome:

HTML:

 <button onclick="uploadFile(event)">Upload files</button>
 <div id="listfiles" class="view_list"></div>
 <input class="hidden" type="file" id="input_file_id" onchange="fileList(event)" name="files[]" multiple>

JS:

    function uploadFile(evt) {
        evt.preventDefault();
        $('#input_file_id').click();
    }

    function fileList(e) {
        var files = $('#input_file_id').prop("files");
        var names = $.map(files, function(val) { return val.name; });
        for (n in names)    {
            $("#listfiles").append("<div id=preload_"+n+" title='"+names[n]+"'><p>"+names[n]+"</p><a  onclick=deleteFile("+n+")>Delete</a></div>");
        }
    }
    function deleteFile(index)  {
        filelistall = $('#input_file_id').prop("files");
        var fileBuffer=[];
        Array.prototype.push.apply( fileBuffer, filelistall );
        fileBuffer.splice(index, 1);
        const dT = new ClipboardEvent('').clipboardData || new DataTransfer(); 
        for (let file of fileBuffer) { dT.items.add(file); }
        filelistall = $('#input_file_id').prop("files",dT.files);
        $("#preload_"+index).remove()
    }
sporteiro
  • 31
  • 1
-9

html

<input id="fileInput" name="fileInput" type="file" />
<input onclick="clearFileInput()" type="button" value="Clear" />

javascript

    function clearFileInput(){
        var oldInput = document.getElementById("fileInput");

        var newInput = document.createElement("input");

        newInput.type = "file";
        newInput.id = oldInput.id;
        newInput.name = oldInput.name;
        newInput.className = oldInput.className;
        newInput.style.cssText = oldInput.style.cssText;
        // copy any other relevant attributes

        oldInput.parentNode.replaceChild(newInput, oldInput);
    }
Ehsan
  • 56
  • 2
  • Not a bad idea... I would change the "replace `oldInput` with `newInput`" with replacing a real `hiddenInput` that is used for the actual form submission, and leave the `oldInput` as is. – skplunkerin Feb 08 '17 at 17:58