Brds
Brds

Reputation: 1075

HTML multiple file upload from different folders

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?

Upvotes: 11

Views: 16113

Answers (6)

Igor Krupitsky
Igor Krupitsky

Reputation: 885

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'>

Upvotes: 0

Khaled Abdel Moezz
Khaled Abdel Moezz

Reputation: 11

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>

Upvotes: 0

baduga
baduga

Reputation: 233

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>

Upvotes: 2

Aarthi A
Aarthi A

Reputation: 106

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>

Upvotes: -1

Isaiah
Isaiah

Reputation: 534

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.

Upvotes: 7

tjati
tjati

Reputation: 6079

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.

Upvotes: 7

Related Questions