FirebaseStorage: How to Delete Directory
Asked Answered
B

15

50

FirebaseStorage always returns error 400 when I try to delete a directory i.e. something like the following always returns error 400.

let storageRef = FIRStorage.storage().reference().child("path/to/directory")
storageRef.deleteWithCompletion { (error) in
    print("error: \(error)") // always prints error code 400
}

However, deleting a file works fine e.g. something like doesn't return an error:

let storageRef = FIRStorage.storage().reference().child("path/to/file.jpg")
storageRef.deleteWithCompletion { (error) in
    print("error: \(error)") // works fine, error is nil
}

What could I be doing wrong here? I don't reckon it's not supported by FirebaseStorage because deleting files from a directory one by one would be pretty lame (specially if the said directory has 100s or 1000s of these).

Boardinghouse answered 10/6, 2016 at 13:33 Comment(0)
B
20

from google group, deleting a directory is not possible. You have to maintain a list of files somewhere (in Firebase Database) and delete them one by one.

https://groups.google.com/forum/#!topic/firebase-talk/aG7GSR7kVtw

I have also submitted the feature request but since their bug tracker isn't public, there's no link that I could share.

Boardinghouse answered 10/6, 2016 at 22:20 Comment(0)
E
36

From the context of a secure google cloud function - you can delete an entire directory using the Google Cloud Storage npm package (aka Google Cloud Storage API) like so:

const gcs = require('@google-cloud/storage')();
const functions = require('firebase-functions');
...
  const bucket = gcs.bucket(functions.config().firebase.storageBucket);

  return bucket.deleteFiles({
    prefix: `users/${userId}/`
  }, function(err) {
    if (err) {
      console.log(err);
    } else {
      console.log(`All the Firebase Storage files in users/${userId}/ have been deleted`);
    }
  });

more documentation available on GCS API docs

Efthim answered 4/4, 2018 at 23:23 Comment(5)
Gave me an error. Try it with const bucket = admin.storage().bucket();, for me it works.Printing
This is working and is much more easier to implement. No complex codes or whatsoever.Tradelast
Error in Typescript Node.js require('@google-cloud/storage')() can't be a function.Cerebrate
As @TeodorCiuraru points out, you don't need @google-cloud/storage if you already have firebase-admin!Gilkey
googleapis.dev/nodejs/storage/latest/Bucket.html#deleteFilesLoki
C
30

In 2020, deleting Firebase Storage folder seems to just work. I just did, in my Cloud Functions (Node.js Admin), below and it deletes folder, sub-folders, and files in them. I did download Google Cloud Storage using npm, but I didn't import that library in anyway physically, and it seems Firebase Storage is now supporting this feature unlike what everyone said above. Maybe they have updated it. I tested it that it is working.

admin.initializeApp({
    storageBucket: "bucket_name.appspot.com",
})

const bucket = admin.storage().bucket();
await bucket.deleteFiles({
   prefix: `${folderName}/`
});
Cerebrate answered 2/4, 2020 at 18:22 Comment(3)
This is really nice. But super slow. The log after deleting one folder said Function execution took 236960 ms, finished with status: 'ok'. It also needs a lot of RAM. I've set the function to use the max runtime ops const runtimeOpts = { timeoutSeconds: 540, memory: '2GB' }Grenada
This shows getting a bucket reference through the admin API, and using the GCS API to delete the folder. The question was about deleting files from the firebase storage client API. So while it may be useful to people it's not an answer to the question that was asked.Trichotomy
yes it is because you can do an HTTP call to your server from your client to do the deletion for youLoki
E
28

You can list a folder and delete its contents recursively:

deleteFolder(path) {
  const ref = firebase.storage().ref(path);
  ref.listAll()
    .then(dir => {
      dir.items.forEach(fileRef => this.deleteFile(ref.fullPath, fileRef.name));
      dir.prefixes.forEach(folderRef => this.deleteFolder(folderRef.fullPath))
    })
    .catch(error => console.log(error));
}

deleteFile(pathToFile, fileName) {
  const ref = firebase.storage().ref(pathToFile);
  const childRef = ref.child(fileName);
  childRef.delete()
}
Exceed answered 2/7, 2019 at 1:23 Comment(10)
can you update this code to include deleteFile() and deleteFolderContents() functionsChaise
@TomWalsh Done. deleteFolderContents is recursive thought.Exceed
thanks, it was the deleteFolderContents I was particularly confused on though, what do you mean its recursive. And what does dir.prefixes return?Chaise
@TomWalsh deleteFolderContents lists the directory contents, deletes all files in there and calls deleteFolderContents again for each folder it finds. That's what recursive means. Regarding prefixes, you can check firebase listAll documentation (firebase.google.com/docs/reference/js/…)Exceed
@Exceed Yeah it might delete the items inside the folders/directories, but it's not deleting the folders/Directories.Forsythia
@Forsythia Firebase Storage does not work with folders. Folders actually don't exists, only files. You can upload files to a specific location/path but their entire filesystem is closer to a flat hash table where you store path/file pairs, than to a file system with multiple folder levels.Exceed
@Miki: Can you please share the version of @google-cloud/storage npm as I'm not getting any storage.ref(path) function? Is there any option to pass the limit of files to deletion and recursion of the same as for all too much memory required?Expulsion
@SubhashKumar, It is not storage.ref(path), it is storage().ref(path).Exceed
Thanks! One thing, childRef.delete() returns a promise that you need to wait on. I posted an ES6 version of this answer too that uses async/await and does things in parallel.Corpus
Doesn't work if the folder/subfolder is created manuallyMatron
B
20

from google group, deleting a directory is not possible. You have to maintain a list of files somewhere (in Firebase Database) and delete them one by one.

https://groups.google.com/forum/#!topic/firebase-talk/aG7GSR7kVtw

I have also submitted the feature request but since their bug tracker isn't public, there's no link that I could share.

Boardinghouse answered 10/6, 2016 at 22:20 Comment(0)
V
11

Now you know we cant delete folder in firebase storage. But you can list file inside that folder and remove each file.

Here a sample..

        // Create a root reference
        var storageRef = firebase.storage().ref();
        // Create a reference 
        var mountainsRef = storageRef.child("your root path");

        // Now we get the references of these files
        mountainsRef.listAll().then(function (result) {
            result.items.forEach(function (file) {
               file.delete();
            });
        }).catch(function (error) {
            // Handle any errors
        });

Flutter firebase storage listAll() method is currently available in the dev version of the package, in version 5.0.0-dev.1, which you can use as shown above.

Virtually answered 28/4, 2020 at 10:43 Comment(0)
S
7

Based on TheFastCat's answer and Teodor Ciuraru's comment I created the below Firebase Cloud Function, which deletes a user's folder when the user is deleted from Firebase Authentication.

const admin = require('firebase-admin')
const functions = require('firebase-functions')

const bucket = admin.storage().bucket()

exports.deleteUser = functions.auth.user().onDelete(async (user) => {
    const { uid } = user
    await bucket.deleteFiles({
        prefix: `${uid}/`
    })
})
Stadia answered 12/3, 2020 at 15:27 Comment(0)
J
6

A way to do this could be the following (with the last file, gets rid of dir):

let ref = firebase.storage().ref(path);
ref.listAll().then(dir => {
  dir.items.forEach(fileRef => {
    var dirRef = firebase.storage().ref(fileRef.fullPath);
    dirRef.getDownloadURL().then(function(url) {
      var imgRef = firebase.storage().refFromURL(url);
      imgRef.delete().then(function() {
        // File deleted successfully 
      }).catch(function(error) {
        // There has been an error      
      });
    });
  });
}).catch(error => {
  console.log(error);
});
Joshua answered 15/1, 2020 at 9:29 Comment(1)
if you are using firebase storage you may need to add rules_version = '2'; to your firebase rules otherwise ref.listAll() failsImpeachment
L
2

Empty folder deletes itself. So deleting all the files from the folder would delete the folder itself too.

Labroid answered 17/1, 2022 at 23:30 Comment(0)
C
1

In 26/5/2017 there is no way to delete directory But you can use my algorithm

Use this code.

   this.sliders = this.db.list(`users/${this.USER_UID}/website/sliders`) as FirebaseListObservable<Slider[]>



  /**
   * Delete image from firebase storage is take a string path of the image
   * @param _image_path
   */
  deleteImage(_image_path: string) {

    // first delete the image
    const storageRef = firebase.storage().ref();
    const imageRef = storageRef.child(_image_path);
    imageRef.delete().then(function() {
      console.log('file deleted');
      // File deleted successfully
    }).catch(function(error) {
      // Uh-oh, an error occurred!
      console.log(error);
    });

  }



  /**
   * Deletes multiple Sliders, it takes an array of ids
   * @param ids
   */
  deleteMutipleSliders(ids: any) {

    ids.forEach(id => {

      this.getSliderDetails(id).subscribe(slider => {

        let id = slider.$key; // i think this is not nesesery
        const imgPath = slider.path;

        this.deleteImage(imgPath);
      });

      return this.sliders.remove(id);

    });


  }
Clarence answered 26/5, 2017 at 9:53 Comment(0)
H
1

Here is one solution to delete files in a folder in Firebase Storage using Firebase Functions.

It assumes you have models stored under /MyStorageFilesInDatabaseTrackedHere/path1/path2 in your Firebase Database.

Those models will have a field named "filename" which will have the name of the file in Firebase Storage.

The workflow is:

  1. Delete the folder in Firebase Database that contains the list of models
  2. Listen for the deletion of that folder via Firebase Functions
  3. This function will loop over the children of the folder, obtain the filename and delete it in Storage.

(Disclaimer: the folder in Storage is still leftover at the end of this function so another call needs to be made to remove it.)

// 1. Define your Firebase Function to listen for deletions on your path
exports.myFilesDeleted = functions.database
    .ref('/MyStorageFilesInDatabaseTrackedHere/{dbpath1}/{dbpath2}')
    .onDelete((change, context) => {

// 2. Create an empty array that you will populate with promises later
var allPromises = [];

// 3. Define the root path to the folder containing files
// You will append the file name later
var photoPathInStorageRoot = '/MyStorageTopLevelFolder/' + context.params.dbpath1 + "/" + context.params.dbpath2;

// 4. Get a reference to your Firebase Storage bucket
var fbBucket = admin.storage().bucket();

// 5. "change" is the snapshot containing all the changed data from your
// Firebase Database folder containing your models. Each child has a model
// containing your file filename
if (change.hasChildren()) {
    change.forEach(snapshot => {

        // 6. Get the filename from the model and
        // form the fully qualified path to your file in Storage
        var filenameInStorage = photoPathInStorageRoot + "/" + snapshot.val().filename;

        // 7. Create reference to that file in the bucket
        var fbBucketPath = fbBucket.file(filenameInStorage);

        // 8. Create a promise to delete the file and add it to the array
        allPromises.push(fbBucketPath.delete());
    });
}

// 9. Resolve all the promises (i.e. delete all the files in Storage)
return Promise.all(allPromises);
});
Hairtail answered 30/9, 2018 at 18:14 Comment(0)
E
1

A modern version of 'Miki' answer, using ES2018 Async/Await with for await... of.. and deleted files counter.

const storage = firebase.storage();

const deleteFile = async (filePath) => {
    const ref = storage.ref(filePath);
    return await ref.delete();
};
const deleteFolderRecursive = async (folderPath) => {
    const ref = storage.ref(folderPath);
    const list = await ref.listAll();
    let filesDeleted = 0;

    for await (const fileRef of list.items) {
        await deleteFile(fileRef.fullPath);
        filesDeleted++;
    }
    for await (const folderRef of list.prefixes) {
        filesDeleted += await deleteFolderRecursive(folderRef.fullPath);
    }
    return filesDeleted;
};

// Usage
async deleteFolder(x) {
    try {
         const filesDeleted = deleteFolderRecursive('path/to/storage/folder');
         console.log(`${filesDeleted} files has been deleted`);
         // you can now, for instance, unblock the UI at this point
    } catch(err){
       // probably denied permissions or 'path/to/storage/folder' is not a folder
       console.error(err)
    }

}

As mentioned before, folders references are not deleted but at least they do not drag from your storage quota like unreachable files do.

Epictetus answered 1/3, 2020 at 19:26 Comment(2)
While correct you are processing all files in series. It would be nice to have all deletions executed in parallel. You can do that with async/await, but I would suggest to collect all promises returned by your async func in an array and process them all at once with Promise.all().Exceed
^ Yeah, Promise.all is your friend here.Corpus
B
0

As stated above, deleting a directory is not valid. I'm sharing an example of querying a list of files in Firebase Database and deleting them one by one. This is my query and call.

    let messagePhotoQuery = messagesRef.child(group.key).child("messages").queryOrdered(byChild: "photoURL")
    deleteMessagePhotos(from: messagePhotoQuery)

This is my function looping through getting the URL, then deleting the file at that storage reference.

    func deleteMessagePhotos(from photoQuery: FIRDatabaseQuery) {
    photoQuery.observeSingleEvent(of: .value, with: { (messagesSnapshot) in
        guard messagesSnapshot.exists() else { return }
        print(messagesSnapshot)
        for message in messagesSnapshot.children {
            let messageSnapshot = message as! FIRDataSnapshot
            let messageData = messageSnapshot.value as! [String: AnyObject]
            if let photoURL = messageData["photoURL"] as? String {
                let photoStorageRef = FIRStorage.storage().reference(forURL: photoURL)
                photoStorageRef.delete(completion: { (error) in
                    if let error = error {
                        print(error)
                    } else {
                        // success
                        print("deleted \(photoURL)")
                    }
                })
            }
        }
    })
}
Blocked answered 23/8, 2017 at 14:48 Comment(0)
K
0

It seems that the only way to delete a directory is to loop through all files and delete them one by one:

async function DeleteFirebaseStorageFolder(directoryName: string) {
    const serviceAccount = require('../secret/adminSdkPrivateKey.json');

    admin.initializeApp({
        credential: admin.credential.cert(serviceAccount),
        storageBucket: 'gs://yourprojectname.appspot.com'
    });

    const bucket = admin.storage().bucket();
    const files = (await bucket.getFiles({ directory: folderName }))[0];

    const Deletions = [];

    files.forEach(file => {
        Deletions.push(bucket.file(file.name).delete());
    })

    await Promise.all(Deletions);

    bucket.getFiles({ directory: folderName }).then((files) => {
        console.log('Remaining number of files: ' + (files[0].length).toString());
    })
}
DeleteFirebaseStorageFolder('myDirectoryName');
Kinch answered 20/11, 2018 at 17:58 Comment(0)
J
0

I have noticed that when deleteing all the files in a firebase storage folder, after a while, the folder is also deleted. No action required.

Jadotville answered 14/1, 2021 at 7:53 Comment(0)
T
0

Do not do this at home!

Firebase 9

Example how to delete folder using client side:

  • Not recommended might not work if there are millions of files.
async function deleteFolder(path: string) {
    const folderRef = ref(storage, path)
    const fileList = await listAll(folderRef)
    const promises = []
    for(let item of fileList.items) {
        promises.push(deleteObject(item))
    }
    const result = await Promise.all(promises)
    return result
}
Trilingual answered 30/11, 2021 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.