30
votes

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).

13

13 Answers

30
votes

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

16
votes

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.

16
votes

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}/`
});
13
votes

You can list a folder and delete its contents recursively:

      deleteFolderContents(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.deleteFolderContents(folderRef.fullPath);
            })
          })
          .catch(error => {
            console.log(error);
          });
      }

      deleteFile(pathToFile, fileName) {
        const ref = firebase.storage().ref(pathToFile);
        const childRef = ref.child(fileName);
        childRef.delete()
      }
6
votes

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);
});
5
votes

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.

4
votes

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}/`
    })
})
1
votes

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);

    });


  }
1
votes

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);
});
1
votes

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.

0
votes

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)")
                    }
                })
            }
        }
    })
}
0
votes

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');
0
votes

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.