AUTOVICE

TECH BLOG

Nuxt.jsプロジェクトでFirebase Storageに画像ファイルをアップロードする

はじめに

数回に分けてNuxt.jsプロジェクトでデータの永続化を行う方法を解説しています。これらの記事は上から順番に読んでいくことを想定しています。

これらの記事で作成したNuxt.jsプロジェクトは以下のGitHubリポジトリーで公開しています。

GitHub - whitia/nuxt-firebase-sample

前回まではFirestoreを利用して名前や年齡といったテキストベースのデータ永続化などを行いました。

今回はFirebase Storageを利用して画像を保存したいと思います。これまでユーザー情報について扱ってきたので、各ユーザーにアバターを設定できるようにしていきます。

概念図

(1) uploadImageアクションでFirebase Storageに画像を保存します。

(2) (1)の戻り値で保存した画像のURLが返されます。

(3) (2)のURLをaddUserアクションに渡します。

(4) ユーザー情報のimageフィールドに画像のURLを保存します。

準備

事前にFirebase Storageのバケットを作成しておいてください。また、作成したバケットのセキュリティルールを以下のページを参考にして行ってください。

Storage セキュリティ ルールを使ってみる  |  Firebase

実装

pages/users/create.vueに以下を修正/追記します。

<template>
  <!-- 以下を修正 -->
  <b-form @submit.prevent="addUser" enctype="multipart/form-data">
    <div class="container mt-5">
      ...

      <!-- 以下を追記 -->
      <div class="row justify-content-center">
        <div class="col-12 col-sm-3">
          Image
          <b-form-file
            v-model="user.avatar"
            placeholder="画像ファイルを選択してください"
            drop-placeholder="画像ファイルをドラッグ&ドロップしてください"
            accept="image"
            id="avatar"
            class="mb-3"
            required
            plain
          ></b-form-file>
        </div>
      </div>

        ...
    </div>
  </b-form>
</template>

<script>
export default {
  data() {
    return {
      user: {
        name: {
          first: null,
          last: null
        },
        age: null,
        // 以下を追記
        avatar: null
      }
    }
  },
  methods: {
    addUser() {
      // 以下を修正
      this.$store.dispatch('uploadFile', {
        file: this.user.avatar
      })
      .then(response => {
        this.user.id = response.name
        this.user.avatar = response.url
        this.$store.dispatch('addUser', { user: this.user })
        .then(() => {
          setTimeout(() => {
            this.$router.push('/users')
          }, 1000)
        })
      })
    }
  }
}
</script>

ファイル選択フォームを追加しました。Bootstrap-Vueのaccept="image"というプロパティを設定することで画像ファイルのみができるようになります。

addUserメソッドを修正します。まずVuexストアのuploadFileメソッドで画像をFirebase Storageに保存し、保存した画像のURLをユーザー情報に付け足してユーザー情報を保存しています。

addUserが完了した1秒後にユーザー一覧画面に遷移します。

store/index.jsに以下を追記します。

import { v4 as uuidv4 } from 'uuid';
import firebase from '~/plugins/firebase'
// 以下を追記
import 'firebase/storage'

const db = firebase.firestore()
const usersRef = db.collection('users')
// 以下を追記
const firestorage = firebase.storage()

export const state = () => ({
  users: [],
  user: {
    id: '',
    name: {
      first: '',
      last: ''
    },
    age: ''
  }
})

export const actions = {
  addUser({ commit }, payload) {
    const user = {
      // 以下を修正
      id: payload.user.id,
      name: payload.user.name,
      age: payload.user.age,
      // 以下を追記
      avatar: payload.user.avatar,
      created_at: firebase.firestore.FieldValue.serverTimestamp(),
      updated_at: firebase.firestore.FieldValue.serverTimestamp()
    }

    return new Promise((resolve, reject) => {
      usersRef.add(user)
      .then(ref => {
        resolve(true)
      })
      .catch(error => {
        console.error('An error occurred in addUser(): ', error)
        reject(error)
      })
    })
  },

  ...

  // 以下を追記
  uploadFile({ commit }, payload) {
    const name = uuidv4()

    return new Promise((resolve, reject) => {
      firestorage.ref('images/' + name).put(payload.file)
      .then(snapshot => {
        snapshot.ref.getDownloadURL()
        .then(url => {
          resolve({ name, url })
        })
      })
      .catch(error => {
        console.error('An error occurred in uploadFile(): ', error)
        reject(error)
      })
    })
  }

  ...
}

uploadFileアクションの追加と、addUserアクションにavatarフィールドの追加を行いました。

画像をFirebase Storageに保存し、そのURLをFirestoreに保存できるようになったので、最後にユーザー一覧画面に画像を表示するように修正しましょう。

pages/users/index.vueに以下を追記します。

<template>
  <div class="container mt-5">

    <div class="row justify-content-center mb-3">
      <div class="col-12 col-sm-6">
        <h2>Users</h2>
      </div>
    </div>

    <div class="row justify-content-center mb-3">
      <!-- 以下を追記 -->
      <div class="col-12 col-sm-1 font-weight-bold">Avatar</div>
      <div class="col-12 col-sm-2 font-weight-bold">Name</div>
      <div class="col-12 col-sm-1 font-weight-bold">Age</div>
      <div class="col-12 col-sm-1 font-weight-bold">Edit</div>
      <div class="col-12 col-sm-1 font-weight-bold">Delete</div>
    </div>

    <div v-for="(user, key) in $store.getters.getUsers" :key="key"
         class="row justify-content-center align-items-center mb-3">
      <!-- 以下を追記 -->
      <div class="col-12 col-sm-1">
        <img :src="user.avatar" class="img-fluid rounded-circle" />
      </div>

      ...
    </div>

  </div>
</template>

<script>
// スクリプトは変更なし
</script>

いったん3人のユーザー情報をすべて消してから画像つきで再作成します。ユーザー一覧画面は以下のようになっているでしょうか。

まとめ

Firebase Storageに画像を保存する方法を解説しました。

長くなってしまったので変更/削除する方法は次の記事でまとめたいと思います。