0
votes

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

3 Answers

1
votes

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.

1
votes

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

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.