0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Nuxt.js】firebase基礎編:Cloud Storageで画像アップロード&取得②

Posted at

#前置き
以前の記事を役割で切り分けて
Vuex版にしたコードです。
image.png

この2つが
一緒になっていて
分かりにくかったので
切り分けてみました✂︎
・選択した画像を送信する
・url化してプレビューする

Vuexに移行する必要があるのは
送信と取得の部分です。

#Step1: 送信するコードを考える
##Vuexに移行する前のコード
選択した画像を
そのままstorageに
putすればOK🙆‍♀️

putするため
引数にFileを使用します💫

e.target.files
FileListオブジェクトは
配列のように管理されていて
0番目に画像データが入っています。
それをそのまま送信📤

index.vue
<template>
  <div class="page">
    <form
      class="form"
      @submit.prevent="submitImg()"
    >
      <input
        type="file"
        accept="img/*"
        @change="changeImg"
      >
      <button
        type="submit"
        class="button"
      >
        click
      </button>
      <div>
        <p>{{ thumbnail }}</p>
      </div>
    </form>
  </div>
</template>

<script>
import firebase from '@/plugins/firebase'

export default {
  data () {
    return {
      thumbnail: '',
    }
  },
  methods: {
    changeImg (e) {
      this.thumbnail = e.target.files[0]
      console.log(this.thumbnail)
      if (this.thumbnail) {
        this.submitImg(this.thumbnail)
      }
    },
    submitImg (thumbnail) {
      let storage = firebase.storage()
      let storageRef = storage.ref().child('file.png')
      storageRef.put(thumbnail)
        .then(res => console.log(res))
        .catch(error => console.log(error))
    },
  },
}
</script>

<style lang="scss" scoped>
</style>

##Vuex移行後のコード

index.vue
<template>
  <div class="page">
    <form
      class="form"
      @submit.prevent="submitImg()"
    >
      <input
        type="file"
        accept="img/*"
        @change="changeImg"
      >
      <button
        type="submit"
        class="button"
      >
        click
      </button>
      <div>
        <p>{{ thumbnail }}</p>
      </div>
    </form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      thumbnail: '',
    }
  },
  methods: {
    changeImg (e) {
      this.thumbnail = e.target.files[0]
    },
    submitImg (thumbnail) {
      this.$store.dispatch('submit', this.thumbnail)
    },
}
</script>

<style lang="scss" scoped>
</style>
store/index.js
import firebase from '@/plugins/firebase'

export const actions = {
  submit ({ context }, image) {
    let storage = firebase.storage()
    let storageRef = storage.ref().child('file.png')
    storageRef.put(image)
      .then(res => console.log(res))
      .catch(error => console.log(error))
  },
}

#Step2: プレビューするコードを書き足す
Vuexに移行する必要がないので
付け足します✍️

画像を読み込み
Fileオブジェクトを
readAsDataURLでurlにします💫

readAsDataURLでの
読み込みが完了したら
onloadで結果を
postData.thumbnailに入れます。
MDN: FileReader.onload

index.vue
<template>
  <div class="page">
    <form
      class="form"
      @submit.prevent="submitImg()"
    >
      <input
        type="file"
        accept="img/*"
        @change="changeImg"
      >
      <button
        type="submit"
        class="button"
      >
        click
      </button>
      <div>
        <p>{{ thumbnail }}</p>
        <img :src="postData.thumbnail" alt="">
      </div>
    </form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      thumbnail: '',
      postData: {
        thumbnail: '',
      },
    }
  },
  methods: {
    changeImg (e) {
      this.thumbnail = e.target.files[0]
      if (this.thumbnail) {
        const reader = new FileReader()
        reader.readAsDataURL(this.thumbnail)
        reader.onload = () => {
          this.postData.thumbnail = reader.result + ''
        }
      }
    },
    submitImg (thumbnail) {
      this.$store.dispatch('submit', this.thumbnail)
    },
}
</script>

<style lang="scss" scoped>
</style>

storeの変更はありません。

#Step3: 取得するコードを考える
これで完成です🎶👏

取得の場合は
firestorageで取得した画像を
getDownloadURLで
url化できるので
それをそのままstateに入れます。

computedで
urlをgettersで呼び出して
img srcにバインドさせればOKです✨

index.vue
<template>
  <div class="page">
    <form
      class="form"
      @submit.prevent="submitImg"
    >
      <input
        type="file"
        accept="img/*"
        @change="changeImg"
      >
      <button
        type="submit"
        class="button"
      >
        click
      </button>
      <div>
        <img :src="postData.thumbnail" alt="">
      </div>
    </form>
    <div>
      <button
        class="button"
        @click="getImg"
      >
        取得
      </button>
      <img :src="getThumbnail" alt="">
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      thumbnail: '',
      postData: {
        thumbnail: '',
      },
    }
  },
  computed: {
    getThumbnail () {
      return this.$store.getters['thumbnail']
    },
  },
  methods: {
    changeImg (e) {
      this.thumbnail = e.target.files[0]
      if (this.thumbnail) {
        const reader = new FileReader()
        reader.readAsDataURL(this.thumbnail)
        reader.onload = () => {
          this.postData.thumbnail = reader.result + ''
        }
      }
    },
    submitImg () {
      this.$store.dispatch('submit', this.thumbnail)
    },
    getImg () {
      this.$store.dispatch('getImg')
    },
  },
}
</script>

<style lang="scss" scoped>
</style>
store/index.js
import firebase from '@/plugins/firebase'

export const state = () => ({
  thumbnail: '',
})

export const getters = {
  thumbnail: state => {
    return state.thumbnail
  },
}

export const actions = {
  submit ({ context }, image) {
    let storage = firebase.storage()
    let storageRef = storage.ref().child('file.png')
    storageRef.put(image)
      .then(res => console.log(res))
      .catch(error => console.log(error))
  },
  getImg ({ commit }) {
    let storage = firebase.storage()
    let storageRef = storage.ref().child('file.png')
    storageRef.getDownloadURL()
      .then(res => {
        console.log(res)
        commit('getData', res)
      })
  },
}

export const mutations = {
  getData (state, image) {
    state.thumbnail = image
  },
}

#調整
image.png
見にくいので少しCSSを調整🎨

index.vue
<template>
  <div class="page">
    <form
      class="form"
      @submit.prevent="submitImg"
    >
      <label class="label">
        <input
          type="file"
          accept="img/*"
          class="input"
          @change="changeImg"
        >
      </label>
      <div class="img">
        <img
          v-show="postData.thumbnail"
          :src="postData.thumbnail"
          alt="preview"
          class="preview"
        >
      </div>
      <button
        type="submit"
        class="button"
      >
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 12l11 3.1 7-8.1-8.156 5.672-4.312-1.202 15.362-7.68-3.974 14.57-3.75-3.339-2.17 2.925v-.769l-2-.56v7.383l4.473-6.031 4.527 4.031 6-22z"/></svg>
      </button>
    </form>
    <div class="getImg">
      <button
        class="button"
        @click="getImg"
      >
        getImg!
      </button>
      <div class="img">
        <img
          v-show="getThumbnail"
          :src="getThumbnail"
          alt="getThumbnail"
          class="img"
        >
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      thumbnail: '',
      postData: {
        thumbnail: '',
      },
    }
  },
  computed: {
    getThumbnail () {
      return this.$store.getters['change/thumbnail']
    },
  },
  methods: {
    changeImg (e) {
      this.thumbnail = e.target.files[0]
      if (this.thumbnail) {
        const reader = new FileReader()
        reader.readAsDataURL(this.thumbnail)
        reader.onload = () => {
          this.postData.thumbnail = reader.result + ''
        }
      }
    },
    submitImg () {
      this.$store.dispatch('change/submit', this.thumbnail)
    },
    getImg () {
      this.$store.dispatch('change/getImg')
    },
  },
}
</script>

<style lang="scss" scoped>
.page {
  padding: 20px;

  > .form {
    background-color: #FFDCDC;
    > .label {
      display: block;
      width: 32px;
      height: 32px;
      background-image: url('data:image/svg+xml, <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="%23000" d="M5 8.5c0-.828.672-1.5 1.5-1.5s1.5.672 1.5 1.5c0 .829-.672 1.5-1.5 1.5s-1.5-.671-1.5-1.5zm9 .5l-2.519 4-2.481-1.96-4 5.96h14l-5-8zm8-4v14h-20v-14h20zm2-2h-24v18h24v-18z"/></svg>');
      background-repeat: no-repeat;
      background-size: cover;

      > .input {
        display: none;
      }
    }

    > .img {
      width: 100px;
      height: 100px;

      .preview {
        width: 100px;
      }
    }
  }

  > .getImg {
    margin-top: 100px;

    > .img {
      width: 100px;
      height: 100px;

      > .img {
        width: 100px;
      }
    }
  }
}
</style>
store/index.js
import firebase from '@/plugins/firebase'

export const state = () => ({
  thumbnail: '',
})

export const getters = {
  thumbnail: state => {
    return state.thumbnail
  },
}

export const actions = {
  submit ({ context }, image) {
    let storage = firebase.storage()
    let storageRef = storage.ref().child('file.png')
    storageRef.put(image)
      .then(res => console.log(res))
      .catch(error => console.log(error))
  },
  getImg ({ commit }) {
    let storage = firebase.storage()
    let storageRef = storage.ref().child('file.png')
    storageRef.getDownloadURL()
      .then(res => {
        console.log(res)
        commit('getData', res)
      })
  },
}

export const mutations = {
  getData (state, image) {
    state.thumbnail = image
  },
}

#まとめ
役割を分けて
切り離すことができました✂︎

前回はonloadを
readAsDataURLの前に
書いていたため、
役割を分ける際に混乱しました😂

流れをしっかり理解するのは大切ですね。
コードは全てを理屈で理解しようとすると詰む
と言いますが、
理論的に分かるに越したことはないです😂

キャプチャの録画は
GIPHY Captureに
変更してみました💫

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?