Uploader with Axios



  • Hi,

    I am using centralized communication backend (GO) using Axios. How can I use Uploader with Axios? Loading just one file and want to trigger upload after uploading that one file.
    Can this be achieved just by using QUploader provided events or methods? Or should I extend QUploaderBase?

    <q-uploader
      label="Upload"
      auto-upload
      accept=".zip"
      :max-file-size="5000"
      style="max-width: 300px"
      />
    

    and my upload function

    upload (file) {
          const fr = new FileReader()
          fr.readAsDataURL(file)
          fr.addEventListener('load', () => {
            this.uploadedImage = fr.result
            this.uploadFile = file
            const admin = this
            const formData = new FormData()
            formData.append('uploadFile', this.uploadFile)
            api.apiClient.post('/api/upload/data', formData)
              .then(response => {
                this.$store.dispatch('context/setNotification', { text: 'Restore successfully completed', type: 'success' })
                // post restore processing
              })
              .catch(function (e) {
                admin.$store.dispatch('context/setNotification', { text: 'Couldn\'t upload file: ' + e.response.data, type: 'error' }, { root: true })
                console.log(e, e.response) /* eslint-disable-line no-console */
              })
          })
        },
    


  • @valasek There are some events that are issued as @added, @finished, @uploaded.

    Look in the documentation: https://quasar.dev/vue-components/uploader#QUploader-API



  • @valasek make use of the upload-factory. edit. so it’s removed now and it’s called factory prop in v1, the new one doesn’t pass the updateProgress function tho :(.



  • I am having a similar issue.
    I have a global axios configured to deal with authentication and QUploader uses direct XHRRequests.
    Even if a do an ugly way to set all security headers in factory function, it gives weird errors os server side.
    If I use the global axios for the same request it works… but then I can not use the (nice) QUploader UI.

    There is any way to tell QUploader to use the global axios instance?



  • Use factory prop passing your own upload function https://quasar.dev/vue-components/uploader#Factory-function.



  • As I understand from docs, factory prop is used to dynamically pass parameters to uploader, not to process the upload in this function (because Quasar will do a request using the values passed by the factory function)
    This is not the same as using the global axios instance (plugin configured on boot).
    If I manually handle the uploader inside the factory, how Quasar will know about it?
    Are the docs incomplete or I just didn’t understand it?



  • @marcelo It seems so, I haven’t tried this new uploader, before v1 there used to be a upload-factory https://v0-17.quasar-framework.org/components/uploader.html#Upload-Factory to handle the upload yourself, I think the new way now is by this https://quasar.dev/vue-components/uploader#Supporting-other-services extending the component and handling the methods on your own in your extended component, see the script below in that section on how. The QUploaderBase source https://github.com/quasarframework/quasar/blob/dev/ui/src/components/uploader/QUploaderBase.js.



  • @marcelo I faced nearly the same problem and find a solution that works for me (Authentication not yet implemented). I’m not sure if it best practice but I also wanted the uploader to be reusable within other components and wanted to use the QUploader component because of its additional features like thumbnail, display file-size…

    I let the url parameter blank.
    I get the selected file’s details through the @added-event and safe it in a vuex store
    I trigger the upload - method outside the QUploader component

    In my uploader.vue file:

    <template>
      <div class="row justify-center items-center">
        <div>
          <div class="row justify-between items-center q-px-sm" v-if="showUploadButton === false">
            <div class="fa-text-dark-light">{{ deleteMethodString }}</div>
            <div>
    //youNeed
              <q-btn
                @click="deleteFile()"
                class="q-pl-sm"
                color="negative"
                flat
                icon="ion-ios-trash"
                round
              >
              </q-btn>
            </div>
          </div>
    //youNeed
          <q-uploader
            :accept="acceptedFileTypes"
            :label="uploaderLabel"
            @added="fileSelected"
            @removed="fileRemoved"
            color="grey-1"
            flat
            hide-upload-btn
            square
            text-color="accent"
            url=""
          >
          </q-uploader>
        </div>
        <div class="q-mt-xl" v-if="showUploadButton === true">
    //youNeed
          <q-btn
            :label="$t('message.general.upload')"
            @click="upload()"
            color="accent"
            outline
          >
          </q-btn>
        </div>
      </div>
    </template>
    
    <script>
    import { mapGetters, mapState, mapActions } from 'vuex'
    export default {
      name: 'uploaderComponent',
      components: {
      },
      data () {
        return {
    //youNeed
          selectedFile: false,
          uploaderLabel: 'Select a File',
          showUploadButton: false,
          deleteMethodString: ''
        }
      },
    //youNeed
      watch: {
        selectedFile () {
          if (this.selectedFile === true) {
            this.uploaderLabel = ''
            this.showUploadButton = true
          } else {
            this.showUploadButton = false
          }
        }
      },
      mounted () {
        this.deleteMethodString = this.deleteString
        this.uploaderLabel = this.uploadLabel
      },
      computed: {
        ...mapGetters({
          currentLanguage: 'language/statusCurrentLanguage'
        }),
        ...mapState({
          fileUploadCompleted: state => state.uploader.fileUploadCompleted,
          concern: state => state.uploader.propsForFileUpload.concern,
          acceptedFileTypes: state => state.uploader.propsForFileUpload.acceptedFileTypes,
          deleteString: state => state.uploader.propsForFileUpload.stringForDeleteMethod,
          uploadLabel: state => state.uploader.propsForFileUpload.uploaderLabel
        })
      },
      methods: {
        ...mapActions({
    //youNeed
          fileUpload: 'uploader/ActionFileUpload',
          uploaderDialogTrigger: 'app/ActionUploaderDialog',
          fileDelete: 'uploader/uploaderFileDelete'
        }),
    //youNeed
        fileSelected (files) {
          if (files.length !== 0) {
            this.selectedFile = true
          }
          this.$store.commit('uploader/MutationFileSelected', files[0])
        },
    //youNeed
        fileRemoved () {
          this.selectedFile = false
        },
    //youNeed
        upload () {
          this.fileUpload()
        },
        deleteFile () {
          this.fileDelete()
          this.uploaderDialogTrigger(false)
        }
      }
    }
    </script>
    

    vuex

    // MUTATION
    export const MutationFileSelected = (state, payload) => {
      state.fileSelected = payload
    }
    
    //ACTION
    export const ActionFileUpload = (context) => {
      let uploaderProps = context.state.propsForFileUpload.concern
      let id = ''
      let uploadUrl = ''
      let uploaData = new FormData()
      if (uploaderProps === 'clubLogoUpload') {
        id = context.rootGetters['clubEdit/getterHomeClubId']
        uploadUrl = 'homeclub/putLogo/'
    //youNeed
        uploaData.append('file', context.state.fileSelected)
      }
    //youNeed
      Vue.prototype.$axios.post(process.env.API + uploadUrl + id, uploaData, {
        headers: {
          'Content-Type': undefined
        },
        onUploadProgress: function (progressEvent) {
          let percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
          context.commit('MutationOnUploadProgress', percentCompleted)
        }
      })
        .then(function (response) {
          if (response.status === 200) {
            console.log(response)
            context.dispatch('app/ActionUploaderDialog', false, {root: true})
            context.dispatch('ActionAfterFileUpload')
            if (context.rootGetters['language/statusCurrentLanguage'] === 'en') {
              Notify.create({
                type: 'positive',
                icon: 'img:statics/icons/check.svg',
                color: 'positive',
                timeout: 1000,
                position: 'center',
                message: 'Yeah. File uploaded. Great Job!'
              })
            }
            if (context.rootGetters['language/statusCurrentLanguage'] === 'zh') {
              Notify.create({
                type: 'positive',
                icon: 'img:statics/icons/check.svg',
                color: 'positive',
                timeout: 1000,
                position: 'center',
                message: 'Yeah. File uploaded. Great Job! -ZH'
              })
            }
          }
        })
        .catch(function (error) {
          console.log(error)
        })
    }
    

    I think there’s a lot of stuff you won’t need but I didn’t want to tear it up. This solution enables me to use axios’s features, which are for me are a bit easier to handle.

    I’ve prefixed the essential parts I assume you need with //youNeed

    If there is anyone who might have an idea to improve this solution, I’d be appreciated



  • @stukki that you a lot, I will try on my project and will get back to you how this worked.


Log in to reply