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 でデュアルマシン マスターとバックアップを実装する方法

推薦する

Linux で完全な Samba サーバーを構築する方法 (CentOS バージョン)

序文smb は、クライアントとサーバー間の Web 接続および情報通信に使用できるプロトコルの名前で...

MySQLの外部結合と内部結合クエリの違い

外部結合の構文は次のとおりです。フィールド名を選択FROM テーブル名 1 LEFT|RIGHT|F...

MySQL クイックデータ比較テクニック

MySQL の運用と保守において、R&D の同僚が 2 つの異なるインスタンスのデータを比較...

MySQL アクティブ-アクティブ同期レプリケーションの 4 つのソリューションの詳細な説明

目次MySQLネイティブレプリケーションに基づくマスター-マスター同期ソリューションGaleraレプ...

LINUX でプロセスを表示する 4 つの方法 (要約)

プロセスは CPU とメモリ内で実行されるプログラム コードであり、各プロセスは 1 つ以上のプロセ...

node.js が大規模プロジェクトに適さない理由

目次序文1. アプリケーションコンポーネント2. アプリケーションの種類3. アプリケーションサービ...

Linux の wget コマンドの詳細な紹介

目次まずwgetをインストールするヘルプマニュアルを見る1. wgetを使用して単一のファイルをダウ...

Rancher のデプロイメントと K8S クラスターのインポートに関する問題

Rancher のデプロイメントには、次の 3 つのアーキテクチャがあります。高可用性 Kubern...

新しい CSS display:box プロパティの詳細な説明

1. ディスプレイボックス;要素にこのプロパティを設定すると、display:inline-bloc...

vue3でDOMをマウントするためのプラグインを書く際の問題について

vue2と比較して、vue3にはアプリの概念が追加され、vue3プロジェクトの作成も // メイン....

MySQL 5.7.18 無料インストールバージョンの設定チュートリアル

MySQL 5.7.18 無料インストール版のインストールチュートリアルMySQL は現在、世界で最...

HTML で JavaScript の全選択/全選択解除操作を実行するサンプル コード

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

フローチャートとUIフローの違い

UI デザインにおける多くの概念は言葉で言えば似ているように見えるかもしれませんが、実際には大きく異...

SpringBoot でマイクロサービスを構築するために Docker を使用した実際の記録を分析する

それは何ですか? Spring Boot は、Spring オープンソース組織のサブプロジェクトであ...

vue3 テレポートの詳細な使用例

公式ウェブサイトhttps://cli.vuejs.org/ja/ガイド/場合によっては、コンポーネ...