【Vue × Storage】Storageに画像を保存・取得を実装する

#Storageに画像を保存・取得を実装する


```html:mypage.vue
<div class="profile-img-inner flex">
  <img :src="preview == '' ? uploadedImage.fileUrl : preview" width="200" height="200" class="profile-img" alt="プロフィール画像"/>
  <!--previewが空の場合、fileUrl（画像）を表示。空の場合はpreviewを表示。-->
    <label class="profile-txt profile-update"> プロフィール画像を編集する
     <input type="file"  @change="onFileChange" style="display:none"/>
    </label>
</div>
```

```javascript:mypage.vue
export default {
  data() {
    return {
      name: "",

    ~ 省略 ~

      preview: "",
      uploadedImage: {
      fileUrl: require("../assets/デフォルトの画像.jpg"),
      time: null,
      }
    };
  },

  methods: {

    ~ 省略 ~   


    onFileChange(e) {
      const image = e.target.files; //選択された画像ファイルを選択
      this.file = image[0]; //画像ファイルを1つだけ選択
      // Firebase storageに保存するパス乱数で決めてthis.uploadUrlへ代入
      const S =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
      const N = 16;
      this.uploadUrl = Array.from(crypto.getRandomValues(new Uint32Array(N)))
        .map((n) => S[n % S.length])
        .join("");
      let self = this;
      let fileReader = new FileReader();
      //FileReaderは、Fileオブジェクトからデータを読み込むことのみを目的としたオブジェクト
      fileReader.onload = function() {
        //fileReader.onloadは、読み込みが正常に完了した時に発火するイベント
        self.preview = fileReader.result;
        //fileReaderの結果をself.previewへ代入
        //関数の中ではfileReaderの[this]を参照してしまうため、一旦[self]に代入して、[this.]の代わりに[self.] とする
      };
      fileReader.readAsDataURL(this.file);
      //this.fileの値をデータURLとして読み込み、488行目が発火する。
    },

    updateBtn() {
            this.$swal({
                title: "内容確認",
                text: "この内容で更新しますか？",
                icon: "info",
                buttons: true,
                dangerMode: true,
            }).then((willDelete) => {
                if (willDelete) {
                    let uploadParam = {};
                    if (this.uploadUrl) {
                        const uploadTask = firebase
                            .storage()
                            .ref(this.uploadUrl) //さっき決めたパスを参照して、
                            .put(this.file); //保存する
                        uploadTask.then(() => {
                            uploadTask.snapshot.ref.getDownloadURL().then((fileUrl) => {
                                //this.fileに保存されたrefを参照してファイルのダウンロード URL を取得して、fileUrlへ代入。
                                this.$set(this, "uploadedImage", {
                                    fileUrl: fileUrl,
                                    time: firebase.firestore.FieldValue.serverTimestamp(),
                                });
                                console.log(fileUrl, this.uploadUrl);
                                uploadParam = { uploadedImage: this.uploadedImage };
                                //uploadParamへuploadedImageを代入。
                                firebase
                                    //画像をfirestoreに保存
                                    .firestore()
                                    .collection("users") //保存する場所を参照して、
                                    .doc(this.$route.params.uid) //追加で保存setメソッドを使うと上書きされる
                                    .set(
                                        {
                                            name: this.name,
                                            sex: this.sex,
                                            age: this.age,
                                            access: this.access,
                                            selfpr: this.selfpr,
                                            profession: this.profession,
                                            genre: this.genre,
                                            favMovie: this.favMovie,
                                            ...uploadParam,
                                            time: firebase.firestore.FieldValue.serverTimestamp(),
                                            //サーバ側で値設定
                                        },
                                        { merge: true }
                                        //set()でmergeをtrueにすると、上書き。updetaと同様。
                                    );
                                const currentUser = firebase.auth().currentUser;
                                currentUser
                                    .updateProfile({
                                        photoURL: fileUrl,
                                    })
                                    .then(() => { })
                                .catch(()=>{
                                });
                            });
                        });
                    }
                    firebase
                        //画像をfirestoreに保存
                        .firestore()
                        .collection("users") //保存する場所を参照して、
                        .doc(this.$route.params.uid) //追加で保存setメソッドを使うと上書きされる
                        .set(
                            {
                                name: this.name,
                                sex: this.sex,
                                age: this.age,
                                access: this.access,
                                selfpr: this.selfpr,
                                profession: this.profession,
                                genre: this.genre,
                                favMovie: this.favMovie,
                                ...uploadParam,
                                time: firebase.firestore.FieldValue.serverTimestamp(),
                                //サーバ側で値設定
                            },
                            { merge: true }
                            //set()でmergeをtrueにすると、上書き。updetaと同様。
                        );
                    this.$swal("更新しました。", {
                        icon: "success",
                    });
                } else {
                    this.$swal("キャンセルしました。");
                }
            })
            .catch(() => {
              this.$swal("更新出来ませんでした。", {
                icon: "error",
              });
              this.preview = ""
              //更新をキャンセルした場合、空にする。
            });
        },
```

#ファイルをアップロードするボタンを作成

```html:mypage.vue
<input type="file" @change="onFileChange" style="display:none" />
```

<input>タグのtype属性でtype="file"を指定すると、 ファイル名の入力フィールドと参照ボタンが表示され、サーバーへ送信するファイルを選択できるようになります。

input type=”file”のデザイン自体はHTMLの仕様でそのままCSSを当ててもデザインが変更されないため、
通常のinputタグをdisplay:noneで隠し、隠し要素として配置してます。

```html:mypage.vue
<img :src="uploadedImage == '' ? preview : uploadedImage.fileUrl" width="200" height="200" class="profile-img" alt="プロフィール画像"/>
  <label class="profile-txt profile-update">プロフィール画像を編集する
   <input type="file" @change="onFileChange" style="display:none"/>
  </label>
```
<label>タグに対してCSSを適用させるように作成すると自由にデザインをすることが出来ます。


#Firebase Storageへ画像を保存


```javascript:mypage.vue
const image = e.target.files; //選択された画像ファイルを選択
this.file = image[0]; //画像ファイルを1つだけ選択
```
Storageに画像を保存するパスは下記乱数で決めてthis.uploadUrlへ代入してます。

```javascript:mypage.vue
const S ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const N = 16;
this.uploadUrl = Array.from(crypto.getRandomValues(new Uint32Array(N)))
　.map(n => S[n % S.length])
  .join("");
```

```javascript:mypage.vue
let self = this;
      let fileReader = new FileReader();
      //FileReaderは、Fileオブジェクトからデータを読み込むことのみを目的としたオブジェクト
      fileReader.onload = function() {
        //fileReader.onloadは、読み込みが正常に完了した時に発火するイベント
        self.preview = fileReader.result;
        //fileReaderの結果をself.previewへ代入
        //関数の中ではfileReaderの[this]を参照してしまうため、一旦[self]に代入して、[this.]の代わりに[self.] とする
      };
      fileReader.readAsDataURL(this.file);
      //this.fileの値をデータURLとして読み込み、434行目が発火する。
    },
```

data()内のpreviewは空配列ですが、上記にて選択された画像を随時格納される。

```javascript:mypage.vue
　　　　　uploadImage() {
                  this.$swal({
                title: "内容確認",
                text: "この内容で更新しますか？",
                icon: "info",
                buttons: true,
                dangerMode: true,
            }).then((willDelete) => {
                if (willDelete) {
                    let uploadParam = {};
                    if (this.uploadUrl) {
                        const uploadTask = firebase
                            .storage()
                            .ref(this.uploadUrl) //さっき決めたパスを参照して、
                            .put(this.file); //保存する
                        uploadTask.then(() => {
                            uploadTask.snapshot.ref.getDownloadURL().then((fileUrl) => {
                                //this.fileに保存されたrefを参照してファイルのダウンロード URL を取得して、fileUrlへ代入。
                                this.$set(this, "uploadedImage", {
                                    fileUrl: fileUrl,
                                    time: firebase.firestore.FieldValue.serverTimestamp(),
                                });
  
```

#Firestoreへプロフィール画像を保存

上記でStorageに保存された画像情報をFirestoreのコレクションを参照して保存してます。

```javascript:mypage.vue
 updateBtn() {

    ~ 省略 ~

                                uploadParam = { uploadedImage: this.uploadedImage };
                                //uploadParamへuploadedImageを代入。
                                firebase
                                    //画像をfirestoreに保存
                                    .firestore()
                                    .collection("users") //保存する場所を参照して、
                                    .doc(this.$route.params.uid) //追加で保存setメソッドを使うと上書きされる
                                    .set(
                                        {
                                            name: this.name,
                                            sex: this.sex,
                                            age: this.age,
                                            access: this.access,
                                            selfpr: this.selfpr,
                                            profession: this.profession,
                                            genre: this.genre,
                                            favMovie: this.favMovie,
                                            ...uploadParam,
                                            time: firebase.firestore.FieldValue.serverTimestamp(),
                                            //サーバ側で値設定
                                        },
                                        { merge: true }
                                        //set()でmergeをtrueにすると、上書き。updetaと同様。
                                    );
                                const currentUser = firebase.auth().currentUser;
                                currentUser
                                    .updateProfile({
                                        photoURL: fileUrl,
                                    })
                                    .then(() => { })
                                .catch(()=>{
                                });
                            });
                        });
                    }
                    firebase
                        //画像をfirestoreに保存
                        .firestore()
                        .collection("users") //保存する場所を参照して、
                        .doc(this.$route.params.uid) //追加で保存setメソッドを使うと上書きされる
                        .set(
                            {
                                name: this.name,
                                sex: this.sex,
                                age: this.age,
                                access: this.access,
                                selfpr: this.selfpr,
                                profession: this.profession,
                                genre: this.genre,
                                favMovie: this.favMovie,
                                ...uploadParam,
                                time: firebase.firestore.FieldValue.serverTimestamp(),
                                //サーバ側で値設定
                            },
                            { merge: true }
                            //set()でmergeをtrueにすると、上書き。updetaと同様。
                        );
                    this.$swal("更新しました。", {
                        icon: "success",
                    });
                } else {
                    this.$swal("キャンセルしました。");
                }
            })
            .catch(() => {
              this.$swal("更新出来ませんでした。", {
                icon: "error",
              });
              this.preview = ""
              //更新をキャンセルした場合、空にする。
            });
        },
```