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

推薦する

ページ内の検索エンジンの呼び出しはBaiduを例に挙げています

今日、突然、自分のウェブページで Google や Baidu のような強力な検索エンジンを呼び出す...

シンプルな CSS テキストアニメーション効果

成果を達成する 実装コードhtml <div id=コンテナ> いらっしゃいませ <...

TypeScript 3.7 で注目すべき 3 つの新機能について簡単に説明します。

目次序文オプションの連鎖ヌル結合呼び出されていない関数のチェック他の序文TypeScript 3.7...

Nginx_geo モジュールを使用して CDN スケジュールを設定する方法

NginxのGeoモジュールの紹介geo ディレクティブは、ngx_http_geo_module ...

モバイル開発チュートリアル: ピクセル表示の問題の概要

序文モバイル端末の開発の過程で、モバイル端末のディスプレイはデスクトップ端末のディスプレイとは一般的...

HTMLとCSSを使用して、自分だけの暖かい男「Dabai」を作成します

最終結果はこんな感じです、かわいいでしょう… PS: HTML と CSS の知識があればベストです...

WebプロジェクトのDockerデプロイメントの実装

前回の記事では、docker サービスをインストールしました。引き続き、Web プロジェクトのデプロ...

MySQL 分離レベルの詳細な説明と例

目次MySQL の 4 つの分離レベルデータ テーブルを作成します。分離レベルの設定物事の分離レベル...

既存のDockerコンテナの内容を変更する方法

1. Docker psはコンテナをリストします 2. Docker cpはコンテナにファイルをコピ...

JS上級編ES6の6つの継承方法

目次1. プロトタイプチェーン継承2. コンストラクタによる継承3. 組み合わせ継承4. プロトタイ...

JS 手ぶれ補正機能の実装と使用シナリオ

目次1. 手ぶれ補正機能とは何ですか? 1. なぜ手ぶれ補正機能が必要なのでしょうか? 2. 手ぶれ...

Linux の非常に詳細な gcc アップグレード プロセス

目次序文1. 現在のgccバージョン2. gccをインストールする3.gmpのインストール4.MPF...

UTF-8 ファイルの Unicode 署名 BOM (バイト オーダー マーク) の問題

最近、UTF8 エンコードの中国語 Zen Cart Web サイトをデバッグしているときに奇妙な現...

タグのターゲットリンクを iframe に向ける方法

コードをコピーコードは次のとおりです。 <iframe id="myFrameId&...

ボタントリガーイベントを使用して背景色の点滅効果を実現します

背景色の点滅効果を実現するには、次のコードを <body> 領域に追加するだけです。コー...