React-Dropzone をベースにアップロードコンポーネント機能を開発する (サンプルデモ)

React-Dropzone をベースにアップロードコンポーネント機能を開発する (サンプルデモ)

今回はReact-Flaskフレームワーク上でアップロードコンポーネントを開発するスキルについてお話します。現在、私は主に React を使用してフロントエンドを開発しています。その過程で、React-Bootstrap、Ant Design、Material UI、Bulma など、多くの興味深いフロントエンド UI フレームワークを知るようになりました。人気のアップロード コンポーネントは多数ありますが、最も多くのユーザーがいるのは jQuery-File-Upload と Dropzone です。一方、急成長している新規参入者には Uppy と filepond があります。

今回はReact-Flaskフレームワーク上でアップロードコンポーネントを開発するスキルについてお話します。現在、私は主に React を使用してフロントエンドを開発しています。その過程で、React-Bootstrap、Ant Design、Material UI、Bulma など、多くの興味深いフロントエンド UI フレームワークを知るようになりました。人気のアップロード コンポーネントは多数ありますが、最も多くのユーザーがいるのは jQuery-File-Upload と Dropzone です。一方、急成長している新規参入者には Uppy と filepond があります。 Fine-Uploader の作者が 2018 年以降メンテナンスを行わないことを決定したのは残念です。後発者として理由を問うつもりはありませんが、オープンソースの作者の皆さんの取り組みを尊重してください。

ここで React-Dropzone を選択する理由は以下のとおりです。

  1. Reactをベースに開発され、高い互換性があります
  2. これはオンラインで高く評価されており、Material UI でもアップロード コンポーネントの開発に使用されています。
  3. 主にDragアンドDropに焦点を当てていますが、送信ロジックは開発者が設計できます。たとえば、socket-io を使用してファイル チャンクを転送してみます。おそらくノード フル スタックでは実現可能ですが、ここでは Flask を使用しており、Blob を ArrayBuffer に変換する必要があります。しかし、私は Python での読み書きの方法を学ぶことはありませんでした。

デモ例

1. Axios は通常のファイルをアップロードします:

yarn を通じて react-dropzone を導入します。

糸 反応ドロップゾーン axios を追加

フロントエンドの js は次のとおりです (不足しているものがある場合は、ご自身で修正してください)。

Reactをインポートします。 
    使用状態、 
    コールバックの使用、
    使用効果、
} を 'react' から取得します。
'react-dropzone' から {useDropzone} をインポートします。
「./dropzone.styles.css」をインポートします
'react-infinite-scroller' から InfiniteScroll をインポートします。
輸入 {
    リスト、
    メッセージ、
    // アバター、
    スピン、
} 'antd' から;
'axios' から axios をインポートします。

/**
* ファイルサイズを計算 * @param {*} バイト 
* @param {*} 小数点 
* @戻り値 
*/
関数formatBytes(バイト、小数点数 = 2) {
    if (bytes === 0) は '0 バイト' を返します。

    定数k = 1024;
    const dm = 小数点 < 0 ? 0 : 小数点;
    const sizes = ['バイト', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    定数 i = Math.floor(Math.log(bytes) / Math.log(k));

    parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i] を返します。
}

/**
* ドロップゾーンアップロードファイル * @param {*} props 
* @戻り値 
*/
関数 DropzoneUpload(props) {
    const [ファイル、setFiles] = useState([])
    const [ロード中、setLoading] = useState(false);
    const [hasMore, setHasMore] = useState(true);

    const onDrop = useCallback(acceptedFiles => {
        読み込みをtrueに設定します。
        フォームデータを作成します。
        smallFiles.forEach(ファイル => {
            formData.append("files", ファイル);
        });
        アクシオス({
            メソッド: 'POST'、
            url: '/api/files/multiplefiles',
            データ: フォームデータ、
            ヘッダー: {
                「コンテンツタイプ」: 「マルチパート/フォームデータ」、
            }
        })
        then(応答 => {
            ファイルを追加します(受け入れられたファイル)。
            読み込みを設定します(false);
        });
    }, [ファイル]);

    // ドロップゾーン設定
    const { getRootProps, getInputProps } = useDropzone({
        複数:true、
        オンドロップ、
    });

    // 添付ファイルを削除する const removeFile = file => {
        const newFiles = [...ファイル]
        newFiles.splice(newFiles.indexOf(ファイル), 1)
        setFiles(新しいファイル)
    }

    使用効果(() => {
        // アップローダーファイルの初期化
        ファイルセット([])
    },[])

    戻る (
        <セクションクラス名="コンテナ">
        <div {...getRootProps({className: 'dropzone'})}>
            <input { ...getInputProps()} />
            <p>ファイルをドラッグするかクリックしてファイルを選択してください😊</p>
        </div>
        
        <div className="デモ無限コンテナ">
            <無限スクロール
                初期ロード={false}
                ページ開始={0}
                loadMore={handleInfiniteOnLoad}
                hasMore={!読み込み中 && hasMore}
                useWindow={false} を使用します
            >
                <リスト
                    データソース={ファイル}
                    レンダリングアイテム={item=> (
                        <リスト.アイテム 
                            アクション={[
                                // <a key="list-loadmore-edit">編集</a>, 
                                <a key="list-loadmore-delete" onClick={removeFile}>削除</a>
                            ]}
                            //追加={
                                
                            // }
                            キー={アイテムパス}>
                            <リスト.アイテム.メタ 
                                アバター={
                                    <>
                                    {
                                        !!item.type && ['image/gif', 'image/jpeg', 'image/png'].includes(item.type) &&
                                        <画像 
                                            幅={100}
                                            alt='ロゴ'
                                            src={item.preview}
                                        />
                                    }
                                    </>
                                }
                                タイトル={item.path}
                                説明={formatBytes(item.size)}
                            />
                        </リスト.アイテム>
                    )}
                >
                    {読み込み中 && hasMore && (
                        <div className="デモ読み込みコンテナ">
                            <スピン />
                        </div>
                    )}
                </リスト>
            </無限スクロール>
        </div>
        </セクション>
    );
}

Flask コード:

複数のファイルを定義する():
'files' が request.files にない場合:
    jsonify({'message': 'ファイルがありません!'}), 200 を返します
ファイル = request.files.getlist('ファイル')

ファイル内のファイルの場合:
    ファイルの場合:
        # secure_filename の中国語問題をピンインで解く filename = secure_filename(''.join(lazy_pinyin(file.filename))
        パス(UPLOAD_FOLDER + '/' + file_info['dir_path']).mkdir(parents=True, existing_ok=True)
        file.save(os.path.join(UPLOAD_FOLDER + '/' + file_info['dir_path'], ファイル名))

return jsonify({'message': '保存に成功しました! !'})

2. 大きなファイルのインポート:

file.slice() メソッドを使用してファイルのチャンクを生成します。 Promise.all は使用しないでください。非連続的なリクエストが発生し、ファイルが破損する可能性があります。

jsコード:

const promiseArray = largeFiles.map(file => new Promise((resolve, deny) => {
                        
    定数チャンクサイズ = CHUNK_SIZE;
    const chunks = Math.ceil(file.size / chunkSize);
    チャンクを 0 にします。
    chunkArray = new Array() とします。
    while (チャンク <= チャンク) {
        offset = chunk * chunkSize とします。
        スライス = file.slice(オフセット、オフセット+チャンクサイズ)
        chunkArray.push([スライス, オフセット])
        ++チャンク;
    }
    const chunkUploadPromises = (スライス、オフセット) => {
        const largeFileData = 新しい FormData();
        largeFileData.append('largeFileData', スライス)
        新しい Promise を返します ((resolve, reject) => {
            アクシオス({
                メソッド: 'POST'、
                url: '/api/files/largefile',
                データ: largeFileData、
                ヘッダー: {
                    「コンテンツタイプ」: 「マルチパート/フォームデータ」
                }
            })
            .then(応答 => {
                コンソールログ(応答);
                解決する(応答);
            })
            .catch(エラー => {
                拒否(エラー);
            })
        })
    };

    chunkArray.reduce( (前のPromise、[次のChunk、次のOffset]) => {
        前のPromise.then(() => { を返します。
            chunkUploadPromises(nextChunk, nextOffset) を返します。
        });
    }, Promise.resolve());
    解決する();
}))

Flask コード:

ファイル名 = secure_filename(''.join(lazy_pinyin(ファイル名)))
パス(UPLOAD_FOLDER + '/' + file_info['dir_path']).mkdir(parents=True, existing_ok=True)
save_path = os.path.join(UPLOAD_FOLDER + '/' + file_info['dir_path'], ファイル名)
試す:
    open(save_path, 'ab') を f として実行します:
        f.seek(オフセット)
        f.write(ファイル.ストリーム.read())
        print("時間: " + str(datetime.now()) + " オフセット: " + str(オフセット))
OSErrorを除く:
    return jsonify({'ファイルに書き込めませんでした'}), 500

結論

ファイル転送、特に大容量ファイルの転送は、常に HTTP の悩みの種でした。最善の方法は、自分でクライアントを作成し、FTP および FTPS プロトコル経由で転送することです。 2 番目の方法は、大企業の集中型の方法です。ファイルのチェックサムを使用して、ファイルがアップロードされたかどうかを判断し、即時アップロード効果を生み出します。分散型 Bittorrent の 3 番目の方法は、各ユーザーがファイル シードとして機能し、ファイル転送を支援するというものです。これは現在、中国ではあまり使用されていません。

React-Dropzone をベースにしたアップロードコンポーネントの開発に関するこの記事はこれで終わりです。React-Dropzone コンポーネント開発に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • React-Dropzone をベースにアップロードコンポーネント機能を開発する (サンプルデモ)
  • Reactフックとzarmコンポーネントライブラリ構成に基づいてh5フォームページを開発するためのサンプルコード
  • React Native 開発パッケージ トーストと読み込み コンポーネントの読み込み例
  • Reactを使ったコンポーネントライブラリ開発の詳しい説明
  • React 開発チュートリアル: React コンポーネント間の通信
  • React 開発で require.ensure() を使用して ES6 コンポーネントをオンデマンドでロードする方法の詳細な説明
  • React 開発で require.ensure を使用して es6 スタイルのコンポーネントをロードする方法

<<:  MySQL データベースのデータ テーブルの最適化、外部キーの分析、3 つのパラダイムの使用

>>:  Nginx+Keepalived でデュアルマシン マスターとバックアップを実装する方法

推薦する

jQueryのanimateアニメーションメソッドとアニメーションキューイング問題の解決方法の詳しい説明

目次animate() アニメーションメソッドアニメーションキューイングdelay() メソッドアニ...

CSSアニメーションに基づくSVGボタンのサンプルコード

具体的なコードは次のとおりです。 <a href="#"> <...

MySQL テーブルスペースの断片化の概念と関連する問題の解決策

目次背景表領域の断片化とは何ですか?表領域の断片化を確認する方法表スペースの断片化問題を解決する方法...

JavaScript でフォロー広告を実装するためのサンプルコード

フローティング広告は、ウェブサイト上で非常に一般的な広告形式です。フローティング広告は、ユーザーの閲...

Docker-compose ネットワークの詳細な例

今日は Docker でのネットワーク設定を試し、後で忘れないようにプロセスを記録しました。 (シス...

Vue グローバルメソッドを設定する 2 つの方法

目次1. はじめに2. 最初の方法3. 2番目の方法要約する1. はじめにVue プロジェクトの開発...

MySQL は低速クエリを可能にします (EXPLAIN SQL ステートメントの使用の概要)

今日、データベース操作はますますアプリケーション全体のパフォーマンスのボトルネックになりつつあり、こ...

JavaScript リフレクション学習のヒント

目次1. はじめに2. インターフェース3. 簡単な例4. 結論1. はじめにMDN の公式 Web...

MySQLデータベースエンジンをInnoDBに変更する

PS: ここではPHPStudy2016を使用しています1. 変更中にMySQLを停止する2. my...

ネイティブ JavaScript を使用した Web 計算機の実装

この記事では、参考までに、計算機のWebバージョンを実装するためのJavaScriptの具体的なコー...

ボタンをEnterキーに関連付けるjsコード

コードをコピーコードは次のとおりです。 <html> <ヘッド> <ス...

Vueライフサイクル機能の詳細な説明

目次ライフサイクル関数一般的なライフサイクルフックVue のインスタンス破棄について:要約するライフ...

Linuxコマンドとファイル検索の詳しい説明

1. ファイル名検索を実行するwhich ('実行可能ファイル' を検索) //PA...

Ubuntu20.04 VNCのインストールと設定の実装

VNC はリモート デスクトップ プロトコルです。 VNC を使用して Ubuntu 20.04 を...

Reactフックの長所と短所

目次序文アドバンテージ:欠点: 1. レスポンシブな使用効果2. ステータスが同期されていないRea...