-
-
Save AllanJeremy/dd5398b1416ac02393a8d896cdcbfe14 to your computer and use it in GitHub Desktop.
| /** This component is now maintained via the [quasar-helpers repo](https://github.com/AllanJeremy/quasar-helpers) */ | |
| import { createUploaderComponent } from "quasar"; | |
| import { computed, ref, watch } from "vue"; | |
| // Firebase stuff | |
| import { | |
| getDownloadURL, | |
| ref as firebaseRef, | |
| uploadBytesResumable, | |
| } from "@firebase/storage"; | |
| import { storage } from "../../firebase"; // or import {getStorage} from '@firebase/storage' | |
| //? importing 👆 from local firebase file because it is attached to our app ~ `storage` here is the value returned by getStorage(firebaseApp) | |
| // Export a Vue component | |
| export default createUploaderComponent({ | |
| // defining the QUploader plugin here | |
| name: "FirebaseUploader", // your component's name | |
| props: { | |
| /** Whether all inputs should be blocked when upload is in progress or not */ | |
| blocking: { | |
| type: Boolean, | |
| default: true, | |
| }, | |
| /** The firebase storage directory your files will be uploaded to */ | |
| //! This assumes that each instance of FirebaseUploader will only upload to a specific directory - customization implementation would be worth considering | |
| directory: { | |
| type: String, | |
| default: "/", | |
| }, | |
| }, | |
| emits: [ | |
| // ...your custom events name list | |
| ], | |
| injectPlugin({ props, emit, helpers }) { | |
| let uploadTaskList = ref([]); | |
| let uploadProgressList = ref([]); | |
| let uploadInProgress = ref(false); | |
| let uploadedFiles = ref([]); | |
| // Using watcher because computed isn't triggered when the progress list array is updated | |
| watch( | |
| () => uploadProgressList, | |
| () => { | |
| uploadInProgress.value = false; | |
| if (uploadProgressList.value.length) { | |
| uploadInProgress.value = uploadProgressList.value.reduce( | |
| (prev, curr) => prev || curr, | |
| false | |
| ); | |
| // Uploads complete - emit uploaded event with file details | |
| emit("uploaded", uploadedFiles); | |
| } | |
| }, | |
| { deep: true } | |
| ); | |
| // [ REQUIRED! ] | |
| // We're working on uploading files | |
| const isUploading = computed(() => uploadInProgress.value); | |
| /** Shows overlay on top of the | |
| uploader signaling it's waiting | |
| on something (blocks all controls) */ | |
| const isBusy = computed(() => { | |
| return props.blocking ? uploadInProgress.value : false; | |
| }); | |
| // [ REQUIRED! ] | |
| // Cancel all uploads | |
| function abort() { | |
| uploadTaskList.value.forEach((uploadTask) => { | |
| uploadTask.cancel(); | |
| }); | |
| } | |
| // [ REQUIRED! ] | |
| // Start the uploading process | |
| function upload() { | |
| // Reset uploads | |
| uploadTaskList.value = []; | |
| uploadProgressList.value = []; | |
| helpers.queuedFiles.value.forEach((fileToUpload, i) => { | |
| // No point uploading the file if it has already been uploaded before | |
| if (helpers.uploadedFiles.value.includes(fileToUpload)) return; | |
| //? 👇 This can be whatever you want ~ can use UUID to generate unique file names | |
| const fileName = `${Date.now()}-${fileToUpload.name}`; | |
| const storageRef = firebaseRef( | |
| storage, | |
| `${props.directory}/${fileName}` | |
| ); | |
| const uploadTask = uploadBytesResumable(storageRef, fileToUpload); | |
| uploadTaskList.value = [...uploadTaskList.value, uploadTask]; | |
| uploadTask.on( | |
| "state_changed", | |
| (snapshot) => { | |
| helpers.updateFileStatus( | |
| fileToUpload, | |
| "uploading", | |
| snapshot.bytesTransferred | |
| ); | |
| uploadProgressList.value[i] = snapshot.state === "running"; | |
| }, | |
| (err) => { | |
| console.error( | |
| "Something went wrong while trying to upload the file.", | |
| err | |
| ); | |
| helpers.updateFileStatus( | |
| fileToUpload, | |
| "failed", | |
| uploadTask.snapshot.bytesTransferred | |
| ); | |
| uploadProgressList.value[i] = false; | |
| }, | |
| async () => { | |
| const uploadUrl = await getDownloadURL(uploadTask.snapshot.ref); | |
| // The upload for all files will be accessible when all files are done uploading | |
| uploadedFiles.value.push({ | |
| originalFile: fileToUpload, | |
| uploadUrl, | |
| }); | |
| const { bytesTransferred } = uploadTask.snapshot; | |
| helpers.updateFileStatus( | |
| fileToUpload, | |
| "uploaded", | |
| bytesTransferred | |
| ); | |
| helpers.uploadedFiles.value = [ | |
| ...helpers.uploadedFiles.value, | |
| fileToUpload, | |
| ]; | |
| helpers.uploadedSize.value += bytesTransferred; | |
| uploadProgressList.value[i] = false; | |
| } | |
| ); | |
| }); | |
| } | |
| return { | |
| isUploading, | |
| isBusy, | |
| abort, | |
| upload, | |
| }; | |
| }, | |
| }); |
Hey Allan, echoing all the praise here for this component, thanks again.
One question I have: The original q-uploader component has a "factory" prop that allows you to do some pre-processing before sending the upload. Imagine you wanted to parse a zip file for containing required items before calling the upload function. How would you achieve this functionality?Answering my own question here. Looks like you can use :filter to achieve this pre-processing i'm after.
I may have spoke too soon...the filter prop can't seem to handle async
Hey Allan, echoing all the praise here for this component, thanks again.
One question I have: The original q-uploader component has a "factory" prop that allows you to do some pre-processing before sending the upload. Imagine you wanted to parse a zip file for containing required items before calling the upload function. How would you achieve this functionality?Answering my own question here. Looks like you can use :filter to achieve this pre-processing i'm after.
Hey @wavwarehousedev , sorry for the delay, I missed this comment among the sea of GitHub notifications.
Thank you for the kind words! I'm glad you found a solution!
An alternate suggestion on how you could handle this would be to add an optional beforeUpload prop as well that would be a function
Answering my own question here. Looks like you can use :filter to achieve this pre-processing i'm after.