20

When I delete a post in my Firebase database I want a cloud function to delete the post's thumbnail in firebase storage accordingly. My issue is when I'm trying to delete the thumbnail I don't think I'm locating the image file correctly.

Here is what I have tried:

const functions = require('firebase-functions')
const admin = require('firebase-admin')
const gcs = require('@google-cloud/storage')()

exports.deletePost = functions.database.ref('Posts/{pushId}').onWrite(event => {

  const original = event.data.val()
  const previous = event.data.previous.val()
  const pushId = event.params.pushId

  if (original === null)
    return

  const filePath = 'Posts/' + pushId + 'thumbnail.jpg'
  const bucket = gcs.bucket('postsapp-12312')
  const file = bucket.file(filePath)
  const pr = file.delete()


  return pr
});

This is what I'm getting in logs

ApiError: Not Found at Object.parseHttpRespBody (/user_code/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:192:30) at Object.handleResp (/user_code/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:132:18) at /user_code/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:465:12 at Request.onResponse [as _callback] (/user_code/node_modules/@google-cloud/storage/node_modules/retry-request/index.js:120:7) at Request.self.callback (/user_code/node_modules/@google-cloud/storage/node_modules/request/request.js:188:22) at emitTwo (events.js:106:13) at Request.emit (events.js:191:7) at Request. (/user_code/node_modules/@google-cloud/storage/node_modules/request/request.js:1171:10) at emitOne (events.js:96:13) at Request.emit (events.js:188:7)

Faisal
  • 857
  • 2
  • 10
  • 21
  • **require** call for `gcs`, for me only worked as: `const gcs = require('@google-cloud/storage');` , thanks to: https://stackoverflow.com/a/41352560/2162226 – Gene Bo Oct 09 '18 at 03:34

5 Answers5

16

For anyone else still head-scratching, I was able to get mine deleted by excluding the bucket name (and hence selecting the default firebase bucket):

 const bucket = admin.storage().bucket();
 const path = "path/to/file.wav";
 return bucket.file(path).delete();
Dane Jordan
  • 1,071
  • 1
  • 17
  • 27
7

I have managed to fix it. The Issue here was that I was writing my bucket address wrong; it should be something like postsapp-12312.appspot.com instead of postsapp-12312

Update For a better way to put your bucket address check @Robert answer

Gene Bo
  • 11,284
  • 8
  • 90
  • 137
Faisal
  • 857
  • 2
  • 10
  • 21
  • That one you use to delete is GCS not firebase functions. You are using functions to listening db change and delete via GCS – vzhen Oct 12 '17 at 15:06
  • @vzhen yes ^ it was the only way to do it at the time, has there been any changes now? can you offer a better answer? – Faisal Oct 12 '17 at 15:17
  • I am looking too. I'm using firebase storage not GCS. Seems like firebase admin sdk storage doesn't support server side delete. – vzhen Oct 12 '17 at 15:33
  • Oh my bad, so you are using GCS to delete firebase storage file. Do you store downloadURL for ref in db? – vzhen Oct 12 '17 at 15:38
  • yes, but It's different in my case. the ref is built upon the user uid + the post key + a generated key. and they are all available in the post node so I build the downloadURl from there – Faisal Oct 12 '17 at 15:49
  • Same here, how do you handle the auto generated token at the end of the downloadURL `alt=media&token=sdfkljsifejwfl` < this one – vzhen Oct 12 '17 at 15:53
  • you don't need the whole downloadURL just the path inside your firebase storage. look at my code in the question, the filePath is basically an example of my storage ref that i use to delete the file – Faisal Oct 12 '17 at 17:11
  • Is that mean for viewing we need the whole URL for deletion it work with bucket and path without alt=media&token=342323 ?? – vzhen Oct 15 '17 at 03:47
  • @vzhen basically yeah – Faisal Oct 15 '17 at 05:33
6

take a look at this functions I have used

const app = admin.initializeApp();

export const onDeleteItem = 
 functions.firestore.document('collectionItems/{collectionID}/items/{itemID}')
   .onDelete((snap, context) => {
     const { collectionID } = context.params;
     const { itemID } = context.params
     return deleteItemImage(collectionID, itemID)
     }
 )

async function deleteItemImage(collectionID: string, itemID: string) {
  const path = `images/${collectionID}/${itemID}`;
  const bucket = app.storage().bucket();
  return bucket.file(path).delete()
    .then(function () {
      console.log(`File deleted successfully in path: ${path}`)
    })
    .catch(function (error) {
      console.log(`File NOT deleted: ${path}`)
    })
}
Dan Alboteanu
  • 9,404
  • 1
  • 52
  • 40
5

You can use the firebase functions environment config to get the bucket name:

const bucket = gcs.bucket(functions.config().firebase.storageBucket)
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
Robert
  • 296
  • 2
  • 6
3

As far as I'm aware, it is not possible to delete firebase storage files from within cloud functions. You can listen to change events on a storage bucket, but you can't interact with it.

The way that I managed this was to remove the 'post' from the database using .set(null). This returns a promise which, when it resolves, you can use to make a delete call to firebase storage.

firebase.database().ref('Posts/{pushId}').set(null).then(() => {
  console.log('Post deleted from database')
  firebase.storage().ref(`Posts/${pushId}thumbnail-image.jpg`).delete().then(() => {
    console.log('Successfully deleted thumbnail');
  }).catch(err => {
    console.log(err);
  });
}).catch(err => {
  console.log(err);
});

Firebase storage won't allow you to recursively delete an entire bucket, so you need to delete each file one-by-one.

  • 1
    Actually, you can, the function I'm using `file.delete()` works. The issue was that I was writing my bucket address wrong. it should be `postsapp-12312.appspot.com` instead of `postsapp-12312`. Sorry that I haven't posted the correct answer. – Faisal May 05 '17 at 14:17