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

推薦する

Dockerイメージの作成Dockerfileとコミット操作

イメージを構築するイメージを構築するには、主に 2 つの方法があります。実行中のコンテナをイメージに...

MySQL ディープ ページング (数千万のデータを素早くページ分割する方法)

目次序文場合最適化まとめ序文バックエンド開発では、一度に大量のデータがロードされ、メモリやディスク ...

MySQL Server 8.0.13.0 インストールチュートリアル(画像とテキスト付き)

MySQL 6.1.3 をベースにした 8.0.13 をインストールします。 MySQL 8.0....

ウェブページ作成のヒントのまとめ

序文この記事は主に、日常の Web ページ制作で遭遇する問題解決スキルの一部をまとめ、皆さんの参考と...

Centos7 でスーパーバイザ デーモンをインストールして設定する方法

初心者は自分で録音しましょう1. スーパーバイザーをインストールします。 Supervisor は ...

CentOS 8にdockerをインストールする最も詳細な方法

CentOS 8にDockerをインストールする公式ドキュメント: https://docs.doc...

サラウンドリフレクションロード効果を実現するHTML+CSS

この記事では、主に html + css を使用してサラウンド リフレクション ローディング エフェ...

マージントップ崩壊現象とその具体的解決策

マージントップの崩壊とはmargin-top の崩壊は、CSS ボックス モデルで発生する現象です。...

Reactにおけるコンテキスト適用シナリオの分析

コンテキストの定義と目的コンテキストは、コンポーネント ツリーにプロパティを明示的に渡すことなく、コ...

Dockerをクリーンアンインストールする方法の詳細な説明

まず、サーバー環境情報: アンインストールの理由:しばらくするとホストマシンのディスクが100%にな...

ReactのPropsの簡単な比較

目次クラスコンポーネントのプロパティ比較浅い同等の浅い比較機能コンポーネントの簡単な比較先週面接に行...

nginx の場所に複数の Proxy_pass メソッドがある

1. まず、nginxの位置情報に関する関連知識を確認しましょう1) 位置マッチング手順: ~ #波...

1つの記事でJavaScriptのクロージャ関数について学ぶ

目次変数のスコープ閉鎖の概念クロージャの使用クロージャのデメリット最後に、クロージャのメリットとデメ...

MySQL エラー 1290 (HY000) の解決方法

私は長い間問題に取り組み、文法上の問題を何度も確認しました。しかし、後でネットで調べてみたら、突然理...