Skip to content
Advertisement

Firebase Cloud Storage – upload with metadata –

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()
  });
Advertisement