0

I am building a Vue.js component that enables uploading and deleting of images on Firebase. So far, my component enables uploading images to database and storage. The image can be deleted only from the database, but still remains in storage. To resolve this, I have attempted to set up the deleteImg()function with a .then()promise to target images in storage for deleting. The example in the docs ('https://firebase.google.com/docs/storage/web/delete-files') is setup to target a hard-coded image. How can I set my delete function to remove specific images targeted by a delete button? Here is my delete function:

    deleteImg(img) {
      db.collection("images").doc(img).delete()
      .then(() => {
        var storageRef = firebase.storage().ref();
        var desertRef = storageRef.child('images/Desert.jpg');
        desertRef.delete().then(function() {
          console.log('Document successfully deleted')
        }).catch(function(error) {
          console.log(error)
        });
      })
      .then(() => {
        this.getImages()
      })
    }

Here is entire component:

<template>
  <div id="app">
    <v-app-bar color="indigo" dark fixed app>
      <v-toolbar-title>Vue Firebase Image Upload</v-toolbar-title>
    </v-app-bar>
    <v-app id="inspire">
      <v-content>
        <v-container fluid>
          <v-layout align-center justify-center>
            <v-flex xs12 sm8 md4>
              <img :src="imageUrl" height="150" v-if="imageUrl" />
              <v-text-field label="Select Image" @click="pickFile" v-model="imageName"></v-text-field>
              <input type="file" style="display: none" ref="image" accept="image/*" @change="onFilePicked"/>
              <v-btn color="primary" @click="upload">UPLOAD</v-btn>
            </v-flex>
          </v-layout>
          <br />

          <v-layout align-center justify-center>
            <v-flex xs12 sm8 md4>
              <div v-for="img in imgUrls" :key="img.id">
                <br />
                <img :src="img.downloadUrl" height="150" />
                <v-btn @click="deleteImg(img.id)">x</v-btn>
              </div>
            </v-flex>
          </v-layout>
        </v-container>
      </v-content>
    </v-app>
  </div>
</template>

<script>
import firebase from 'firebase'
import { db } from "./main";

export default {
  name: "App",
  data() {
    return {
      photo: null,
      photo_url: null,
      dialog: false,
      imageName: "",
      imageUrl: "",
      imageFile: "",
      imgUrls: []
    };
  },
  created() {
    this.getImages();
  },
  methods: {
    getImages: function() {
      db.collection("images")
        .get()
        .then(snap => {
          const array = [];
          snap.forEach(doc => {
            const data = doc.data()
            array.push({
              id: doc.id,
              ...data
            });
          });
          this.imgUrls = array;
        });
      this.imageName = ""
      this.imageFile = ""
      this.imageUrl = ""
    },
    pickFile() {
      this.$refs.image.click();
    },
    onFilePicked(e) {
      const files = e.target.files;
      if (files[0] !== undefined) {
        this.imageName = files[0].name;
        if (this.imageName.lastIndexOf(".") <= 0) {
          return;
        }
        const fr = new FileReader();
        fr.readAsDataURL(files[0]);
        fr.addEventListener("load", () => {
          this.imageUrl = fr.result;
          this.imageFile = files[0]; // this is an image file that can be sent to server...
        });
      } else {
        this.imageName = "";
        this.imageFile = "";
        this.imageUrl = "";
      }
    },
    upload: function() {
      var storageRef = firebase.storage().ref();
      var mountainsRef = storageRef.child(`images/${this.imageName}`);
      mountainsRef.put(this.imageFile).then(snapshot => {
        snapshot.ref.getDownloadURL().then(downloadURL => {
          this.imageUrl = downloadURL;
          const bucketName = "xxx-xxxx-xxxxx.xxxxxxx.xxx";
          const filePath = this.imageName;
          db.collection("images").add({
            downloadURL,
            downloadUrl:
              `https://firebasestorage.googleapis.com/v0/b/${bucketName}/o/images` +
              "%2F" +
              `${encodeURIComponent(filePath)}?alt=media`,
            originalName: this.imageName,
            timestamp: Date.now()
          });
          this.getImages();
        });
      });
    },
    deleteImg(img) {
      db.collection("images").doc(img).delete()
      .then(() => {
        var storageRef = firebase.storage().ref();
        var desertRef = storageRef.child('images/Desert.jpg');
        desertRef.delete().then(function() {
          console.log('Document successfully deleted')
        }).catch(function(error) {
          console.log(error)
        });
      })
      .then(() => {
        this.getImages()
      })
    }
  },
  components: {}
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>


UPDATED DELETE FUNCTION

async deleteImg(img) {
  let imgDBRef = await db.collection("images").doc(img).get()
    let imgFileName = imgDBRef.exists ? imgDBRef.data().originalName : null;
    let storageRef = firebase.storage().ref();
    let desertRef = storageRef.child('images/' + imgFileName);
    desertRef.delete()
    db.collection("images").doc(img).delete()
    this.getImages()
}

3 Answers 3

1

I think you should try to save an image file name to the DB in order to have ability to map it to the storage item.

Add imageName from here:

storageRef.child(`images/${this.imageName}`);

to db.collection("images").add(..) function argument object:

db.collection("images").add({
            downloadURL,
            originalName: this.imageName,
          });

and then in your deleteImg function just get the originalName from db.collection("images").doc(img) document data something like this:

const imgDBRef = db.collection("images").doc(img).get();
const imgFileName = imgDBRef.exists ? imgDBRef.data().originalName : null;
//... here should be your checks for image name nullable state etc.
const storageRef = firebase.storage().ref();
const desertRef = storageRef.child('images/' + imgFileName);

And it's better to store file names as hashes, not real names.

Sign up to request clarification or add additional context in comments.

10 Comments

Thank you for responding! I incorporated your answer, but the console continually returns the null. I will update my delete function in the answer to show you how I added it. Any recommendations on how to fix that?
Are you sure that you are getting a reference by db.collection("images").doc(img) code to the image? Try to check that by console.log and then if the reference is not null, try to get document values with originalName.
I setup console.log(imgFileName) to see what was being assigned to that imgFileName based on let imgFileName = imgDBRef.exists ? imgDBRef.data().originalName : null; . The console continually returns null, which means imgFileName never actually gets originalName. Is there perhaps another way that you would recommend assigning a reference to imgfileName instead of using a ternary statement?
Try to replace let imgFileName = imgDBRef.exists ? imgDBRef.data().originalName : null; to let imgData = imgDBRef.exists ? imgDBRef.data() : null; and then check which fields are at data console.log(imgData); We should check, that imgData really contains some fields that you set during upload.
Thanks again. console.log still returns null for this.
|
1

Here is the final answer:

async deleteImg(img) {
  let imgDBRef = await db.collection("images").doc(img).get()
  let imgFileName = imgDBRef.exists ? imgDBRef.data().originalName : null;
  let storageRef = firebase.storage().ref();
  let desertRef = storageRef.child('images/' + imgFileName);
  await desertRef.delete()
  await db.collection("images").doc(img).delete()
  await this.getImages()
}

1 Comment

A lot of the other answers here are misleading or deprecated. This solution works great! Thanks, @JS_is_awesome18 :)
0

To delete an image from storage, you'll need to know the path to that image in the Cloud Storage bucket. If you only have the download URL for the image, you can get a reference with firebase.storage().refFromUrl(...) and then call delete() in the result.

12 Comments

Hi Frank, thanks for the feedback. I understand how to utilize the storage path in the context of the upload function, but not for the delete function. Could you expand on this a little? I attempted to apply what you said to my deleteImg() function, but to no avail. See my updated function at the bottom of the answer..
Your deleteImg code looks fine at first glance. What doesn't work about that code? When you step through it in a debugger, what line doesn't do what you expect it to do?
I am getting two errors. First says Error in v-on handler: "[object Object] . Second is a FirebaseStorageError (see above)
What doesn't work is deleting from storage. The file path in the database is removed, as well as the image on the screen. But the image still stays in storage. I want that deleted as well.
So, stepping through the code in the debugger, what path does desertRef point to? Is that indeed the image to remove? If so, does it go into the then() or into the catch() block after that line?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.