LoginSignup
8
7

More than 5 years have passed since last update.

React+GCPで、画像のプレビュー表示と、画像保存を行う

Last updated at Posted at 2018-12-01

はじめに

SNSとかでよくある、
「プロフィール画像を選択」
→「画像のプレビューが出る」
→「外部ストレージに保存する」

というのを、React + Firestore(DB) + Firebasestorage(Storage)で行う方法です。

なにか他に良い方法などあれば、ご教授いただけると非常にありがたいです!

ちなみに、今回は画像をそのまま送っていますが、
react-image-cropこのライブラリを使えば、
画像サイズの調整なども簡単にできます

なにをすればいいのかざっくり説明

今回のテーマは、「プロフィール画像の変更」なので、
プロフィールには、
・プロフィール画像
・名前
の2つのフィールドがあることとします。

ここで行う処理を先に整理しておくと、

  1. componentDidMountで、現在のプロフィール画像を取ってくる

  2. input[type='file']で、画像ファイルを受け取る

  3. 受け取ったファイルを参照できる仮のURLを作成して、renderしているphotoURLをそちらに変更.
    storageに送る用のfileを別で保持しておく。

  4. 「保存ボタン」を押したら、保持しているfileをFirebaseStorageにおくり、それを参照できるURLを作る

  5. Firestoreを更新

やってみよう

render -> componentDidMountまで

ProfileSetting.jsx
export default class ProfileSetting extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      photoURL: 'https://{イメージがない時に表示する画像パス}',
      blob: {},
      imgChanged: false,
      saving: false
    }
  }
  handleChangeFile(e) {
    // ファイルが上がった時の処理
  }
   submit = async () => {
    // 保存する
  }

  componentDidMount = async () => {
    // ユーザを取ってくる処理
    const user = Backend.getUser()
    this.setState({ ...user.data() })
  }
  render() {
    const { photoURL, blob, saving } = this.state
    return (
      <div style={styles.wrapper}>
        <h1  style={styles.heading}>プロフィール画像を設定する記事を書く</TitleText>
        <img style={styles.profileImg} src={photoURL} />
        <input type="file" disabled={saving} onChange={(e) => this.handleChangeFile(e)} />
      </Wrapper>
    )
  }
}

Backend.js

getUser = _ => {
  // this.uid は、プロフィール編集者のuidとします
  return firestore.collection("users").doc(this.uid).get()
}

ここまでで、下みたいな感じになります(スタイルはこちらで適当に当ててますが)

ここで、画像の処理中(saving = trueのとき)に、
inputを押させないようにします。

スクリーンショット 2018-10-18 21.00.34.png

ファイルがあがったときの処理(handleChangeFile)

ProfileSetting.js
...
const createObjectURL = (window.URL || window.webkitURL).createObjectURL || window.createObjectURL;

handleChangeFile(e){
  const file = e.target.files[0];
  const image_url = createObjectURL(file);
  this.setState({
    photoURL: image_url,
    file: file
  })
}

...

input[type='file']にしたときは、ファイルはe.target.filesの配列になって入っています。
multiple属性をつけていると、複数メンバが入りますが、今回は一個のファイルしか扱わないので、
e.target.files[0]で良いです。

そして、createObjectURLで、ファイルの内容を参照可能な一時URLを作ります。

そのときの画像がこちら
スクリーンショット 2018-10-18 21.17.32.png

プロフィール画像が、さっきのスクリーンショットになっていますね。
これで、とりあえずプレビューは完成です。

inputからfirebaseに送る処理(submit)

ProfileSetting.js
...

submit = async() => {
  this.setState({ saving: true })
  const { changedImg, file, displayName } = this.state
  const photoURL = await Backend.uploadImage({type: 'image', file: file})
    await Backend.updatePhotoURL({
    displayName: displayName, 
    photoURL: photoURL
  })
  this.setState({ saving: false })
  alert("保存しました!")
}

Firestoreと、FirestorageでストレージとDBの更新処理

Backend.js

// ストレージにイメージをアップロードして、それを参照可能なURLをreturnする!
uploadImage = async ({type, file}) => {
  const storageRef = firebase.app().storage().ref(`${type}/${this.user.uid}`)
  await storageRef.put(file)
  return storageRef.getDownloadURL()
}

// ユーザのデータを更新する
updatePhotoURL = ({displayName, photoURL}) => {
  return usersRef.doc(this.uid).update({
    displayName: displayName,
    photoURL: photoURL
    updatedAt: firebase.firestore.FieldValue.serverTimestamp()
  })
}

このようにして、更新できます!

8
7
1

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
8
7