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 calledfactory
prop in v1, the new one doesn’t pass theupdateProgress
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 componentIn 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.
-
@stukki Thanks a lot, I try on my project but 3 vuex errors returns when send a file (store/uploader/action) :
[vuex] unknown local mutation type: MutationOnUploadProgress, global type: uploader/MutationOnUploadProgress [vuex] unknown action type: app/ActionUploaderDialog [vuex] unknown local action type: ActionAfterFileUpload, global type: uploader/ActionAfterFileUpload
Could you help me ?
-
I have the same problem. Is there any solution?
-
@stukki said in Uploader with Axios:
@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 componentIn 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
Hi… that doesn’t work… when I try formData.append it goes empty to backend… There is problem on this method. I think because of security reason it is not appendable… How did you run it?