-
-
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, | |
}; | |
}, | |
}); |
This is great. You might consider putting this into a repo so people can make pull requests. In any case, thanks for sharing.
Allan thanks for your initiative,... looking your code it fits perfect to quasar devs description and I guess this code will runs smooth but it's my first time implementing quasar's q-upload and like me - maybe - other newbies will find this masterpiece, so the question for helping me and others are: How we mix this to regular q-upload component and about the composable counterpart "html" into template session, can you bring us an example? Early thanks(so much)
Just seen these comments now. A little too late :(
Turning this into a repo @guswelter . Thanks y'all!
Created a repo with the same
@saro-saravanan , really sorry I saw this late amidst the flood of github notifications.
I'm really happy you got to resolve it and I have pushed your suggestion in the repo :)
Feel free to fork the repo and create PRs in case you have any suggestions :)
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?
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 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
With the help of a friend, we figured out a problem with the component emitting the "uploaded" signal before the upload was actually completed. The following two lines:
should be replaced with: