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

#前置き
[以前の記事](https://qiita.com/aLiz/items/42685e5d871730b548dc)を役割で切り分けて
Vuex版にしたコードです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/954f7bc6-6685-7db3-033b-1330246201ab.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](https://developer.mozilla.org/ja/docs/Web/API/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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469755/d289173e-8438-0df1-75c4-b7d56d8f1be7.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に
変更してみました💫