I wish to upload files from the browser with metadata that will allow the files to be identified and handled correctly with cloud functions.
On the client my uploader code looks like this:
uploadTaskPromise: async function (file) { return new Promise((resolve, reject) => { const storageRef = storage.ref(); const myRef = storageRef.child(`myfolder/${my_file_name}.wav`); const metadata = { project_id: my_project_id }; const uploadTask = myRef.put(file, { metadata: metadata }); uploadTask.on("state_changed",(snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log("Upload is " + progress + "% done"); }, function error(err) { console.log("error", err); reject(); }, function complete() { uploadTask.snapshot.ref.getDownloadURL() .then(function (downloadURL) { resolve(downloadURL); }); } ) }); },
And on the server (Firebase Cloud Functions using Node js)
exports.uploadHandler = functions.storage.object().onFinalize(async (object) => { const bucket = admin.storage().bucket(object.bucket); const metadata = await bucket.file(object.name).getMetadata() console.log("METADATA: " + JSON.stringify(metadata)) // THIS IS WHERE I WANT TO HANDLE UPLOADS BASED ON THE METADATA return ({ message: "file uploaded" }) });
The uploadHandler
is working – but metadata
is undefined
, as is metadata.metadata
How can I access the metadata that I uploaded from the client?
Advertisement
Answer
You were really close. As per the API reference, the second argument is an UploadMetadata
object which has a property called customMetadata
.
So, to correct your code, you’d swap out
myRef.put(file, { metadata: metadata });
with
wmyRef.put(file, { customMetadata: metadata });
Additionally, your progress logic shouldn’t call resolve
and reject
like it is at the moment – that’s an antipattern. You should instead chain to UploadTask
‘s own Promise API.
return wmyRef.put(file, { customMetadata: metadata }) .then( (snapshot) => { // onComplete return snapshot.ref.getDownloadURL(); }, (err) => { // onError (although I would just omit this entirely) console.log("failed to upload", err); throw err; } );
With your original code, if getDownloadURL()
failed, its error wouldn’t be handled.
If you still want progress reporting, use:
const uploadTask = wmyRef.put(file, { customMetadata: metadata }); uploadTask.on( "state_changed", (snapshot) => { const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100; console.log("Upload is " + progress + "% done"); } ); return uploadTask .then(snapshot => { console.log("Upload is complete"); return snapshot.ref.getDownloadURL() });