11

I have a form with an upload field that allows users to select multiple files. However, I need to be able to allow the user to select file 1 from folder 1, then go and select file 2 from folder 2, and so on.

Currently, when the user selects file 1 from folder 1 then hits "Open", the selection window closes (leaving the user on my form). Then if the user goes and select file 2 from folder 2 and hits the "Open" button, file 1 is removed, leaving only file 2.

Basically, the user is unable to select multiple files unless they're all in the same location. Is there a way to make file 1 stay selected after file 2 is chosen?

2
  • I'm pretty sure that this is impossible, the file selection window is controlled by the OS. Commented Jul 22, 2014 at 13:51
  • 1
    that is correct but its necesary Commented Jul 28, 2017 at 21:58

6 Answers 6

7

No, you can't. This is a behaviour defined by the operating systems and may vary between them. You can't control these things precisly and you will always fear what happen.

If the amount of folders people have to choose is quite small you could offer multiple upload fields.

Sign up to request clarification or add additional context in comments.

Comments

7

How about this?

The solution uses HTML, jQuery/Javascript, and PHP (for server-side handling of this data). The idea is: 1.) HTML Form: Includes one "browse" button that allows the user to select multiple files (within one directory). 2.) jQuery: The option to create a new button in the form that allows users to select multiple files (within a different directory -- or even the same one actually!), with the ability to create new buttons "infinitely". 3.) PHP: As a bonus, I put some thought into packaging the data nicely for server-side handling.

Here is what the HTML form could look like (I used a found-icon for the clickable object, but you can easily replace it with a graphic of your choosing).

<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>" enctype='multipart/form-data'>
    Select files: <br/>
    <input type='file' name='files0[]' id="files0" multiple><br/><br/><br/>
    <span style="font-size: 10pt;">Click "+" for more files
    <i id="more_files" class="general foundicon-plus" style="color: blue;cursor: pointer;"></i></span>
    <br/><br/><br/>
    <input type="submit" name="submit" value="Submit">
</form>

Here is the jQuery/Javascript to create a new "browse" button once the event is triggered (this even places it after the LAST "browse" button!):

<script type="text/javascript">
//jQuery
$(document).ready(function() {
    $(document).on('click','#more_files', function() {
        var numOfInputs = 1;
        while($('#files'+numOfInputs).length) { numOfInputs++; }//once this loop breaks, numOfInputs is greater than the # of browse buttons

        $("<input type='file' multiple/>")
            .attr("id", "files"+numOfInputs)
            .attr("name", "files"+numOfInputs+"[]")
            .insertAfter("#files"+(numOfInputs-1));

        $("<br/>").insertBefore("#files"+numOfInputs);
    });
});
</script>
<script>
    //vanilla javascript version
    var location = document.getElementById("fileBrowsers");
    var br = document.createElement("BR");
    location.appendChild(br);
    var input = document.createElement("input");
    input.type = "file";
    input.name = "files"+numOfInputs+"[]";
            input.id = "files"+numOfInputs;
            input.multiple = true;

            location.appendChild(input);
</script>

Finally, and possibly most important, how to wrap up the data on the server in a familiar format:

<?php
if(isset($_POST['submit']) && !empty($_FILES)) {
    $files = array();
    $files = $_FILES['files0'];
    //var_dump($files);//this array will match the structure of $_FILES['browser']
    //Iterate through each browser button
    $browserIterator = 1;
    while(isset($_FILES['files'.$browserIterator])) {
        //Files have same attribute structure, so grab each attribute and append data for each attribute from each file
        foreach($_FILES['files'.$browserIterator] as $attr => $values) {//get each attribute
            foreach($_FILES['files'.$browserIterator][$attr] as $fileValue) {//get each value from attribute
                $files[$attr][] = $fileValue;//append value
            }
        }
        $browserIterator++;
    }
    //Use $files like you would use $_FILES['browser'] -- It is as though all files came from one browser button!
    $fileIterator = 0;
    while($fileIterator < count($files['name'])) {
        echo $files['name'][$fileIterator]."<br/>";
        $fileIterator++;
    }
}
?>

Update Note: jQuery script and vanilla Javascript accomplish the same goal. I ran into an issue that required the vanilla version. You only need one of them.

Comments

2

Another solution is using old school (non-multiple) file inputs. In this case you cannot select multiple files to upload, but you can remove any file and add another. Initially there is only one file input on page, but when you select a file, it hiding and replacing by filename with delete button, and new file input appears.

var fileInput = document.getElementById('fileInput_0');
var filesList =  document.getElementById('fileList');  
var idBase = "fileInput_";
var idCount = 0;

var inputFileOnChange = function() {

    var existingLabel = this.parentNode.getElementsByTagName("LABEL")[0];
    var isLastInput = existingLabel.childNodes.length<=1;

    if(!this.files[0]) {
        if(!isLastInput) {
            this.parentNode.parentNode.removeChild(this.parentNode);
        }
        return;
    }

    var filename = this.files[0].name;

    var deleteButton = document.createElement('span');
    deleteButton.innerHTML = '&times;';
    deleteButton.onclick = function(e) {
        this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
    }
    var filenameCont = document.createElement('span');
    filenameCont.innerHTML = filename;
    existingLabel.innerHTML = "";
    existingLabel.appendChild(filenameCont);
    existingLabel.appendChild(deleteButton);
    
    if(isLastInput) {   
        var newFileInput=document.createElement('input');
        newFileInput.type="file";
        newFileInput.name="file[]";
        newFileInput.id=idBase + (++idCount);
        newFileInput.onchange=inputFileOnChange;
        var newLabel=document.createElement('label');
        newLabel.htmlFor = newFileInput.id;
        newLabel.innerHTML = '+';
        var newDiv=document.createElement('div');
        newDiv.appendChild(newFileInput);
        newDiv.appendChild(newLabel);
        filesList.appendChild(newDiv);
    } 
}

fileInput.onchange=inputFileOnChange;
#fileList > div > label > span:last-child {
    color: red;
    display: inline-block;
    margin-left: 7px;
    cursor: pointer;
}
#fileList input[type=file] {
    display: none;
}
#fileList > div:last-child > label {
    display: inline-block;
    width: 23px;
    height: 23px;
    font: 16px/22px Tahoma;
    color: orange;
    text-align: center;
    border: 2px solid orange;
    border-radius: 50%;
}
<form enctype="multipart/form-data" method="post">
    <div id="fileList">
        <div>
            <input id="fileInput_0" type="file" name="file[]" />
            <label for="fileInput_0">+</label>      
        </div>
    </div>
</form>

Comments

0

Here is the complete Solution

<!DOCTYPE html>
<html>
<head>
  <title>Custom File Selection</title>
</head>
<body>
  <input type="file" id="customFileInput" multiple style="display: none">
  <button onclick="openCustomFileInput()">Select Files</button>
  <ul id="selectedFilesList"></ul>

  <script>
    var selectedFiles = []; // Array to store the selected files

    function openCustomFileInput() {
      var fileInput = document.getElementById('customFileInput');
      fileInput.click();
    }

    function handleCustomFileInput(event) {
      var fileList = event.target.files;
      var selectedFilesList = document.getElementById('selectedFilesList');

      for (var i = 0; i < fileList.length; i++) {
        var file = fileList[i];
        selectedFiles.push(file); // Add the newly selected file to the array

        var listItem = document.createElement('li');
        listItem.textContent = file.name;

        var deleteButton = document.createElement('button');
        deleteButton.textContent = 'Delete';
        deleteButton.addEventListener('click', createDeleteHandler(file, listItem));
        listItem.appendChild(deleteButton);

        selectedFilesList.appendChild(listItem);
      }
    }

    function createDeleteHandler(file, listItem) {
      return function() {
        var index = selectedFiles.indexOf(file);
        if (index !== -1) {
          selectedFiles.splice(index, 1); // Remove the file from the array
        }

        listItem.parentNode.removeChild(listItem); // Remove the list item from the list
      };
    }

    document.getElementById('customFileInput').addEventListener('change', handleCustomFileInput);
  </script>
</body>
</html>

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

Here is code that worked for me:

  SetupMultiFile(file);

    function SetupMultiFile(file) {
        var list = [];
    
        file.addEventListener('click', function () {
            list = [];
            for (var i = 0; i < file.files.length; i++) {
                list.push(file.files[i]);
            }
        });
    
        file.addEventListener('change', function () {
            if (list.length == 0) return;
    
            var oNames = {};
            var oDataTransfer = new DataTransfer();
    
            //copy new
            for (var i = 0; i < file.files.length; i++) {
                oDataTransfer.items.add(file.files[i]);
                oNames[file.files[i].name] = true;
            }
    
            //copy old
            for (var i = 0; i < list.length; i++) {
                if (oNames[list[i].name]) { } else {
                    oDataTransfer.items.add(list[i])
                }           
            }
    
            file.files = oDataTransfer.files;
        });
    }
<input type=file multiple id='file'>

Comments

-1

document.querySelector("input").addEventListener("change", list_files);

function list_files() {
  var files = this.files;
  for (var i = 0; i < files.length; i++) {
    console.log(files[i].name);
  }
}
<input type="file" multiple>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.