15
12

More than 3 years have passed since last update.

続:ReactNative(Expo)でカメラで撮った写真をFiresbase Storageに保存する

Last updated at Posted at 2020-01-02

前にも記事を書いたのですが、いろいろなところからの寄せ集めだったので改めてまとめてみました。

主な流れ

Expo + Firebaseを利用したアップロード処理は以下のようになります。

  • CAMERAパーミッションを取得
  • ImagePckerを起動し写真を取る(ファイルを選ぶ)
  • アップできるよう取得したファイルをblobに変換
  • アップロード先を指定してアップロード
  • プログレス等を処理
  • downloadURLを取得して表示に利用

サンプルの仕様

ボタンとか配置するのがめんどくさいのでreac-native-elementsのAvatarを利用して画像の選択、表示をやってみます。
まあProfile画像とかでも使いますしね。Imageとかでも同じです(コメントアウトして入れてます)。

cap.png

いちおうプログレスとかも拾ってみます。

実装

必要モジュールのインストール

シンプルなわりにいろいろ使ってます。

npm install --save expo-constants expo-permissions expo-image-picker
npm install --save firebase react-native-elements

Firebase

Firebaseへの登録やプロジェクト作成は当然として、App.jsと同じ階層にFirebase.jsを設置し、以下のようにしています。
Config情報は各自の環境に合わせます。

Firebase.js
import firebase from 'firebase';

const firebaseConfig = {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxx.firebaseapp.com",
    databaseURL: "https://xxxxxxxx.firebaseio.com",
    projectId: "xxxxxxxxx",
    storageBucket: "xxxxxxxxx.appspot.com",
    messagingSenderId: "xxxxxxxxxx",
    appId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    measurementId: "xxxxxxxxx"
};

firebase.initializeApp(firebaseConfig);
export default firebase;

実装

流れがわかるように1つの関数に実装しています。

以前実装した際はPermissionはCAMERA_ROLLを取得する必要がありましたが、今回はCAMERAじゃないとエラーになりました。

App.js
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';

import { Avatar, Button } from 'react-native-elements';
import firebase from './Firebase';
import Constants from 'expo-constants';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';

class App extends React.Component {

    state = {
        url: 'https://firebasestorage.googleapis.com/v0/b/test-fc01e.appspot.com/o/noimage.png?alt=media&token=0606e1b4-e817-4859-83d4-7920c99e14c0',
        progress: '',
    }

    ImageChoiceAndUpload = async () => {

        try {

            //まず、CAMERA_ROLLのパーミッション確認
            if (Constants.platform.ios) {
                const { status } = await Permissions.askAsync(Permissions.CAMERA);
                if (status !== 'granted') {
                    alert("利用には許可が必要です。");
                    return;
                }
            }

            //次に、画像を選ぶ
            const result = await ImagePicker.launchCameraAsync();
            if (!result.cancelled) {

                //撮影された(ローカルの)写真を取得
                const localUri = await fetch(result.uri);
                //blobを取得
                const localBlob = await localUri.blob();

                //filename 実際はUIDとかユーザー固有のIDをファイル名にする感じかと
                const filename = "profileImage"

                //firebase storeのrefを取得
                const storageRef = firebase.storage().ref().child("images/" + filename);

                //upload
                // const putTask = await storageRef.put(localBlob);
                //進捗を取得したいのでawaitは使わず
                const putTask = storageRef.put(localBlob);
                putTask.on('state_changed', (snapshot) => {
                    let progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                    this.setState({
                        progress: parseInt(progress) + "%",
                    });
                }, (error) => {
                    console.log(error);
                    alert("アップロードに失敗しました。サイズが大きいとか。");
                }, () => {
                    putTask.snapshot.ref.getDownloadURL().then(downloadURL => {
                        console.log(downloadURL);
                        this.setState({
                            progress: '',
                            url: downloadURL,
                        });
                    })
                })
            }
        } catch (e) {
            console.log(e.message);
            alert("サイズが多き過ぎるかも。");
        }

    }

    render() {
        return (
            <View style={{ marginTop: 100, alignSelf: 'center' }}>
                <Text>画像アップロード</Text>
                <View style={{ margin: 20 }}>
                    <Avatar
                        size="large"
                        rounded
                        title="NI"
                        onPress={this.ImageChoiceAndUpload}
                        source={{ uri: this.state.url }}
                    />
                    <Text style={{ alignSelf: 'center' }}>{this.state.progress}</Text>
                </View>
                {/* <Image
                    source={{ uri: this.state.url }}
                    style={{ width: 100, height: 100, alignSelf: 'center' }}
                /> */}
            </View>
        );
    }
}

export default App;

その他

アップロードの容量制限等をしたいところですが、Storageのルールを使うこともできるようです。あとはextensionのresizeとかもいいかもしれません。

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read;
      allow write: if request.resource.size < 3 * 1024 * 1024; //3Mまで
    }
  }
}
15
12
0

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
15
12