7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React+Firebase Storageでファイルをドラッグしてアップロードする

Last updated at Posted at 2020-05-14

概要

Reactで以下の内容を実装するサンプルです。ファイルは画像を前提にしています。

1.ファイルをドラッグするとファイルをプレビュー
2.登録ボタンを押すとstorageにアップロードし、最後にDB書き込み

インストール

ファイルをドラッグしてファイル情報を取得するコンポーネントは react-dropzoneを使います。

firebaseもインストールします。

npm i -S react-dropzone
npm i -S firebase

オプショナルですがbootstrapを入れておきます

npm i -S bootstrap

ドラッグでファイル情報を取得する

まず、ファイルをドラッグしたらファイル情報を取得するところを実装します。
onDropはドラッグしたときに呼ばれる関数で、callbackを利用しています。

import React, { useState, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import 'bootstrap/dist/css/bootstrap.min.css';

function App() {

    const [uploadfile, setUploadfile] = useState();
    const maxSize = 3 * 1024 * 1024;

    //dropzone
    const onDrop = useCallback((acceptedFiles) => {

        if (acceptedFiles.length > 0) {
            setUploadfile(acceptedFiles[0]);
        }

    }, []);

    //initialize
    const { acceptedFiles, getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({
        onDrop,
        accept: 'image/png, image/jpeg, image/gif, image/jpg',
        minSize: 1,
        maxSize,
    });

    return (

        <div className="container m-5 text-center">
            <div {...getRootProps()}>
                <input {...getInputProps()} />
                <p>ファイルをドラッグするかクリックしてください。</p>
                {uploadfile ? <p>選択されたファイル: {uploadfile.name}</p> : null}
            </div>
        </div>

    );
}

export default App;

ファイルがドラッグされるとコールバックでonDropが呼ばれ、acceptedFielsのなかに、ファイル情報が入ってきます。
これをuploadfileというステートに入れてファイル情報を画面に表示しています。

実行すると、以下のように選択されたファイルが表示されます。

実行画面

条件に一致しないファイルをエラーにする

initializeのところで、maxSizeacceptを指定しています。
maxSizeはドロップできる最大サイズ、acceptはファイルタイプを指定しています。
これを指定することで自動的にファイルをリジェクトしてくれます。

ドラッグすると、ファイルタイプが一致しない場合はisDragRejecttrue、ファイルサイズではねられるものはfileRejectionsにエラー情報が入ってきます。
isDragReject はドラッグ中のみ取得できます。


        <div className="container m-5 text-center">
            <div {...getRootProps()}>
                <input {...getInputProps()} />
                <p>ファイルをドラッグするかクリックしてください。</p>
                {uploadfile ? <p>選択されたファイル: {uploadfile.name}</p> : null}
                {isDragReject ? <div className="alert alert-danger" role="alert">ファイルタイプが一致しません</div> : null}
                {fileRejections.length > 0 ? <div className="alert alert-danger" role="alert">
                    {fileRejections[0].errors[0].message}
                </div> : null}
            </div>
        </div>

fireRejectionsに入っているerrorsがエラー情報です。codeとmessageが英語で入っているので、実際の実装時にはcodeをみてメッセージを表示すると良いと思います。

スクリーンショット 2020-05-14 7.05.43.png

ドラッグした時にプレビュー表示する

次にプレビュー表示します。
画像を表示するためにファイル情報をURL形式にし、state(fileUrl)に設定します。

stateの設定とonDrop を次ように変更します。


    const [fileUrl, setFileUrl] = useState();

//省略

    const onDrop = useCallback((acceptedFiles) => {

        if (acceptedFiles.length > 0) {
            const src = URL.createObjectURL(acceptedFiles[0]);
            setFileUrl(src);
            setUploadFile(acceptedFiles[0]);
        }

    }, []);

取得した画像を表示する部分です

        <div className="container m-5 text-center">
            <div {...getRootProps()}>
                <input {...getInputProps()} />
                <p>ファイルをドラッグするかクリックしてください。</p>
                {uploadfile ? <p>選択されたファイル: {uploadfile.name}</p> : null}
                {isDragReject ? <div className="alert alert-danger" role="alert">ファイルタイプが一致しません</div> : null}
                {fileRejections.length > 0 ? <div className="alert alert-danger" role="alert">
                    {fileRejections[0].errors[0].message}
                </div> : null}
+               <div className="card mt-2" style={{ margin: 'auto', width: '200px', height: '200px' }} >
+                   <img src={fileUrl} width="100%" />
+               </div>
            </div>
        </div>

実行したところです。

スクリーンショット 2020-05-14 9.30.25.png

storageにアップロードしてDBに書き込む

次にstroageにアップロードして、参照用のdownloadUrlを取得してDBに書きこみます。
firebase用の設定は割愛します。
作成したfirebaseの設定をimportで予めimportしておきます。

登録部分を実装します。

//省略
import firebase from './firebase'; //firebaseの設定

function App() {

//省略

    const [message, setMessage] = useState();

    //submit
    const upload = async () => {

        //fire upload
        let url = "";
        if (uploadfile.name) {
            const storageref = firebase.storage().ref('sample/' + uploadfile.name);
            const snapshot = await storageref.put(uploadfile);
            url = await snapshot.ref.getDownloadURL();
        }

        //db updated
        if (url) {
            await firebase.firestore().collection('sample').doc().set({
                filename: uploadfile.name,
                fileUrl: url,
            });

            setMessage('登録しました');
        }
    }


先にstorageにアップし、アップした時にgetDownloadUrlで参照用のstorageのURLを取得します。
それをfirestoreに登録しています。

登録ボタンと登録後のメッセージ表示の部分です。


        <div className="container m-5 text-center">
            <div {...getRootProps()}>
                <input {...getInputProps()} />
                <p>ファイルをドラッグするかクリックしてください。</p>
                {uploadfile ? <p>選択されたファイル: {uploadfile.name}</p> : null}
                {isDragReject ? <div className="alert alert-danger" role="alert">ファイルタイプが一致しません</div> : null}
                {fileRejections.length > 0 ? <div className="alert alert-danger" role="alert">
                    {fileRejections[0].errors[0].message}
                </div> : null}

                <div className="card mt-2" style={{ margin: 'auto', width: '200px', height: '200px' }} >
                    <img src={fileUrl} width="100%" />
                </div>
            </div>
+            <button type="button" className="btn btn-primary mt-2" onClick={upload}>登録</button>
+            {message ? <div className="alert alert-success mt-2" role="alert">{message}</div> : null}
        </div>

実行してみた画面です。

スクリーンショット 2020-05-14 10.11.07.png

storageにもfirestoreにも書き込まれています。

storage
スクリーンショット 2020-05-14 10.09.53.png

firestore
スクリーンショット 2020-05-14 10.08.38.png

以上です。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?