Upload multiple files, picking them one by one, with BootstrapVue b-form-file

Viewed 1733

I'm uploading files with b-form-file in BootstrapVue, setting multiple to true works perfectly for multiple files, BUT each time I choose a file it removes any previously added ones. And the files will often be spread across multiple folders, so I need to be able to pick a file from one folder, choose it, pick another file from another folder, and so on.

Here's the HTML:

<b-form ref="form" novalidate action="upload" method="post" enctype="multipart/form-data">
    <b-form-file name="file_upload[]" :multiple="true" :file-name-formatter="formatAssetUpload" no-drop placeholder="Click to choose"></b-form-file>
</b-form>

I've tried adding

ref="fileUpload" 

to the b-form-file tag and then in the formatAssetUpload function just setting the value, but that doesn't work. There's a setFiles function in there but it doesn't seem to affect anything. I tried catching the form on submit and manually adding the files to formdata but that's not working either, whatever I try there's always only the last file/files that were picked coming through on the backend.

Any ideas?

Thanks for any help! :)

1 Answers

If you like to remember the file objects which have been selected by the user you an use this javascript:

new Vue({
  el: '#app',
  data() {
    return {
      files: [],
      filesAccumulated: []
    }
  },
  methods: {
    onChange(event) {
      this.files.forEach(thisFile => {
        this.filesAccumulated.push(thisFile)
      })       
    },
    onReset() {
        this.filesAccumulated = []
    }
  }
})

Together whit this vue template

<div id="app">
  <b-form-file
    v-model="files"
    multiple plain
    v-on:input="onChange">
  </b-form-file>
  <div>
    <ul>
      <li v-for="thisFile in filesAccumulated">
      {{ thisFile.name }}
      </li>
    </ul>
  </div>
  <b-button v-on:click="onReset">
    Reset
  </b-button>
</div>

The b-form-file vue component is emitting an input event when the user performs the selection. The file objects can be picked up from the variable bound with the v-model directive. See documentation

Look at this fiddle https://jsfiddle.net/1xsyb4dq/2/ to see the code in action.

General comments

Let me make some comments on your code example and b-form-file component in general: With bootstrap-vue and <b-form-file> it is not the idea to use an enclosing form tag to submit forms. Instead you use vue's v-model feature to obtain the file objects from vue's data object.

I have created a more comprehensive example here https://jsfiddle.net/to3q7ags/ , but I will explain step by step:

The value of the v-model attribute is the name of a vue data property:

<b-form-file v-model="files" multiple plain></b-form-file>

After the user has clicked 'Browse' in the file input field, has selected the files and pressed ok, the vue data property will hold an array of file objects:

new Vue({
  el: '#app',
  data() {
    return {
      files: [] 
      // initially empty, but will be populated after file selection
    }
  }
})

From the b-form file documentation:

When no files are selected, an empty array will be returned. When a file or files are selected the return value will be an array of JavaScript File object instances.

After files are selected by the user you can send the files to your server on a button click or change event.

<b-button v-on:click="onSubmit">
    Submit
</b-button>

the corresponding vue method looks like this

methods: {
  onSubmit() {
    // Process each file
    this.files.forEach(thisFile => {
      console.log("Submitting " + thisFile.name)
      // add submit logic here
    }
}

To submit the files you need to manually create a http post request which is using the multipart/form-data encoding. That can be done by using FormData class.

const formBody = new FormData()
formBody.set("formParameter", thisFile)

The FormData.set() method is accepting file blobs as arguments. Then you can use XMLHttpRequest.send() or fetch() to send the post request:

fetch(
  "https://yourserver/post",{
    method: 'POST',
    body: formBody})
    .then(response => console.log(response))

Now all selected files are posted to the server. The user can repeat the process with the same form, select new set of files and again press the post button.

You can also send the form automatically without the use of a button by listening to the 'input' event. Drag and drop also works.

Related