はじめに
SNSとかでよくある、
「プロフィール画像を選択」
→「画像のプレビューが出る」
→「外部ストレージに保存する」
というのを、React + Firestore(DB) + Firebasestorage(Storage)で行う方法です。
なにか他に良い方法などあれば、ご教授いただけると非常にありがたいです!
ちなみに、今回は画像をそのまま送っていますが、
react-image-cropこのライブラリを使えば、
画像サイズの調整なども簡単にできます
なにをすればいいのかざっくり説明
今回のテーマは、「プロフィール画像の変更」なので、
プロフィールには、
・プロフィール画像
・名前
の2つのフィールドがあることとします。
ここで行う処理を先に整理しておくと、
-
componentDidMountで、現在のプロフィール画像を取ってくる
-
input[type='file']で、画像ファイルを受け取る
-
受け取ったファイルを参照できる仮のURLを作成して、renderしているphotoURLをそちらに変更.
storageに送る用のfileを別で保持しておく。 -
「保存ボタン」を押したら、保持しているfileをFirebaseStorageにおくり、それを参照できるURLを作る
-
Firestoreを更新
やってみよう
render -> componentDidMountまで
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>
)
}
}
getUser = _ => {
// this.uid は、プロフィール編集者のuidとします
return firestore.collection("users").doc(this.uid).get()
}
ここまでで、下みたいな感じになります(スタイルはこちらで適当に当ててますが)
ここで、画像の処理中(saving = trueのとき)に、
inputを押させないようにします。
ファイルがあがったときの処理(handleChangeFile)
...
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を作ります。
プロフィール画像が、さっきのスクリーンショットになっていますね。
これで、とりあえずプレビューは完成です。
inputからfirebaseに送る処理(submit)
...
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の更新処理
// ストレージにイメージをアップロードして、それを参照可能な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()
})
}
このようにして、更新できます!