Vue を使ってFirebaseのストレージにファイルを保存する

記事
IT・テクノロジー

Vue を使ってFirebaseのストレージにファイルを保存する

Vue を使って Firebase のストレージにファイルを保存する
前回の記事では、Vue でページ毎にアクセスの権限を使い分けるやり方を紹介しました。今回は、前回の実装を少し発展させて、「管理者モード」を考えてみます。この記事では、Firebase のストレージに管理者としてファイルをアップロードする方法を紹介します。

管理者モードの利用例

まずは、管理者モードで何をするかの例を紹介します。 例えば、ブログサービスを提供する事を考えた場合には、ブログの記事を投稿する機能と、投稿された記事を閲覧する機能が必要になります。

この場合、ブログの記事を投稿するのは基本的にブログサービスの「管理者」が投稿するという形になります。

この場合、管理者は「特定の利用者」になって、閲覧者(一般の利用者)は、それ以外の利用者というように分ける事ができます。

この記事では、まずは、ファイルをインターネットのサーバーに保存する方法を紹介します。

Firebase のストレージ機能を使う

インターネットにファイルを保存するには、「保存する場所」が必要になります。今回は、この場所として Firebase のストレージ機能を利用する事にします。

前回までに、Firebase のプロジェクトを作成しているので今回は Firebase のストレージ機能を新たに有効にします。これは、Firebase コンソールから可能です。最初はインターネット上に公開せずにテストするので、「テストモード」を選択すれば、当面はセキュリティールールを設定しないでも基本的なテストが可能になります。

テストモードで設定されるセキュリティルールです。この例では、2022 年の 5 月 17 日までは、「誰でも読み書き可能」というルールです。

したがって、このままインターネットで公開した場合、Firebase のプロジェクト情報があれば、誰でもストレージのデータを操作できるようになっているため、セキュリティ上の問題があります。したがって、インターネットに公開する前にセキュリティルールの正しい設定が必要になります。

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if
          request.time < timestamp.date(2022, 5, 17);
    }
  }
}
プロダクションモードを選択した場合は、基本的なアクセスは読み書きともに禁止されているので、必要に応じてアクセスを許可するためのルール設定をしないと Firebase のストレージ機能を利用する事はできません。

アップロードフォームを作成する
まずは、 Vue でファイルをアップロードするためのフォーム(部品)を作成します。

<template>
  <div>
    <div>
      <label>ファイル</label>
      <input
        class="form-control"
        type="text"
        v-model="filename"
        placeholder="ファイルが選択されていません"
        readonly
      />
      <input
        class="hidden"
        ref="file_input"
        type="file"
        @change="file_selected"
      />
      <button class="btn btn-primary" @click="$refs.file_input.click()">
        アップロード
      </button>
    </div>
  </div>
</template>

<script>
import { uploadFile } from "../lib/firebase";
export default {
  data() {
    return {
      filename: "",
    };
  },
  methods: {
    file_selected(e) {
      const file = e.target.files[0];
      this.filename = file.name;
      uploadFile("sample/" + file.name, file).then((url) => {
        if (url) {
          console.log(url);
        } else {
          console.log("Upload error");
        }
      });
    },
  },
};
</script>
<style scoped>
.hidden {
  display: none;
}
</style>
ファイルの選択には「input」のタグで、「type="file"」と指定する事もできますが、表記が英語になっているので、少し工夫をして通常の「input」のタグで、選択したファイル名を表示できるようにています。「input type="file"」の部分は、CSS の記述で表示されないようにしています。

ファイルが選択された時点でファイルをアップロードする処理を別に用意して置いて呼び出すようにしています。あとは、このフォームの部品を呼び出して表示できるようにしておけば良いだけです。

今回は、前回作成した、ログインが必要なページの「AboutView.vue」を変更して、「AdminView.vue」に変更して、表示のためのパスも、「/about」から、「/admin」に変更しています。

変更点は

* ファイル名の変更( src/views/AboutView.vueから src/views/AdminView.vue)
* src/router/index.jsの変更
* src/App.vueの変更
基本的に、「About や about」の部分を「Admin や admin」に書き換えます。

Firebase の処理の追加

前回は、Firebase の初期化のために、 src/lib/firebase.jsを作成していますが、今回はこのファイルに、ファイルをアップロードするための処理を追加します。

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import {
  getDownloadURL,
  getStorage,
  ref,
  uploadBytesResumable,
} from "firebase/storage";

....

// Initialize Firebase
export const firebase = initializeApp(firebaseConfig);
export const auth = getAuth();
export const storage = getStorage();

export function uploadFile(path, file) {
  return new Promise((resolve) => {
    const fileRef = ref(storage, path);
    const uploadTask = uploadBytesResumable(fileRef, file);

    uploadTask.on(
      "state_changed",
      (snapshot) => {
        // Current completion in a percentage of the total transfer
        const progress =
          (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        console.log("Upload is ", progress + " % done.");
        switch (snapshot.state) {
          case "paused":
            console.log("Upload is paused.");
            break;
          case "running":
            console.log("Upload is running.");
            break;
        }
      },
      (error) => {
        // Error -- Handle unsuccessful uploads
        resolve(undefined);
      },
      () => {
        // Handle successful uploads on complete
        // --> Gets the download URL
        getDownloadURL(uploadTask.snapshot.ref)
          .then((downloadURL) => {
            resolve(downloadURL);
          })
          .catch((error) => {
            resolve(undefined);
          });
      }
    );
  });
}
Firebase のストレージを使うためのオブジェクトを作成して、ファイルをアップロードして、ファイルにアクセスするための URL を返すようになっています。ファイルのアップロードが失敗した場合は、URL の代わりにundefinedを返すようにしています。

あとは、「src/views/AdminView.vue」から、アップロードフォームの部品を呼び出せば完了です。

<template>
  <div v-if="login.loginState" class="admin">
    <h1>This is an about page</h1>
    <UploadForm />
  </div>
  <div v-if="!login.loginState">
    <LoginForm />
  </div>
</template>
<script>
import { useLoginStore } from "../stores/login";
import LoginForm from "../components/loginForm.vue";
import UploadForm from "../components/uploadForm.vue";
export default {
  components: { LoginForm, UploadForm },
  setup() {
    const login = useLoginStore();
    return {
      login,
    };
  },
  data() {
    return {
      loginInfo: {
        email: "",
        password: "",
      },
    };
  },
};
</script>

これでログインしていない場合には、アップロードフォームは表示されないので、アップロードする事はできません。

まとめ
今回は、Vue を利用した本格的なサービスを作成する第一段階として、Firebase のストレージ機能を利用して、インターネット上にファイルをアップロードして保存する機能の実装のやり方を紹介しました。

実際にファイルを Firebase のストレージにアップロードできるのはログインしたユーザーだけにしています。Firebase を利用すると、初心者でも、Firebase の機能を呼び出すだけで、こうした機能も簡単に実現できます。

次回は、アップロードして保存したファイルの一覧を表示する方法を紹介します。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す