R

ReflexKR

Reflex公式ドキュメント

コンポーネント工房:Reflexライブラリ100

·

講座の概要

レイアウト

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

タイポグラフィ

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

フォーム

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

データ表示

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

開閉と切り替え

·

Tabs

·

Accordion

·

Segmented Control

オーバーレイ

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

動的レンダリング

·

Cond

·

Match

·

Foreach

·

Auto Scroll

メディア

·

Image

·

Audio

·

Video

表とデータグリッド

·

Table

·

Data Table

·

Data Editor

チャート

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

チャートの部品

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

ほかのチャートライブラリ

·

Plotly

·

Pyplot

HTML要素

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

そのほかの道具

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

コンポーネント工房:Reflexライブラリ100
フォーム

Upload

rx.uploadrx.upload.root

公式ドキュメント

工房の注文書

返金申請の画面に、レシート写真の添付欄を作ってください。画像は3枚まで、ドラッグ&ドロップですぐ添付され、名前とサイズが表示されるようにしてください。

rx.uploadは、ファイルをドラッグ&ドロップするかクリックして選ぶ領域(ドロップゾーン)です。選んだファイルはまずブラウザに留まり、イベントハンドラーにrx.upload_files()を渡したときに初めてサーバーへ送られます。サーバーのハンドラーはファイルのリストを受け取って、読んだり保存したりします。

この講座の例は共用サーバーで動いているので、ファイルは保存せず、名前とサイズだけを表示します。保存するコードは別途コードとしてだけ示します。

rx.upload(
    rx.text("Drop files here or click to select"),
    id="upload_basic",
    border="2px dashed var(--gray-7)",
    padding="2em",
)

プロパティを変えてみる

ドロップゾーンの動作を決めるプロパティです。ファイルをいくつか選んでから、multipleをオフにしたりno_clickをオンにしたりしてみてください。選んだファイル名は下にバッジで表示されます。

プロパティを変えてみる

label

multiple

disabled

no_click

no_drag

no_keyboard

rx.vstack(rx.upload(rx.text("Drop files here or click to select"), id="upload_lab", multiple=True, disabled=False, no_click=False, no_drag=False, no_keyboard=False, border="2px dashed var(--gray-7)", padding="2em"), rx.foreach(rx.selected_files("upload_lab"), lambda f: rx.badge(f, variant="soft")))
no_click はクリックでの選択を、no_drag はドラッグ&ドロップを、no_keyboard は Space・Enter で開くことを止めます。0.10 では multiple の既定値は True です。
プロパティ値既定値用途
id文字列(固定)"default"このドロップゾーンの名前。下の特殊な Var・イベントが同じ id を使う
accept{MIME: [拡張子...]}すべてのファイル受け付けるファイルの種類
multipleTrue / FalseTrue複数選択
max_files整数制限なし最大ファイル数
max_size, min_sizeバイト制限なしファイル1つの最大・最小サイズ
disabledTrue / FalseFalseドロップゾーンを無効にする
no_click, no_drag, no_keyboardTrue / FalseFalseクリック / ドラッグ&ドロップ / キーボードで開くのを止める
drag_active_styleスタイルの dictなしファイルをドラッグして入ってきたときのスタイル(0.10)
on_dropイベント選んだファイルを保持ファイルをドロップするか選んだらすぐ実行
on_drop_rejectedイベントエラーのトースト条件に合わないファイルが入ってきたとき

選んだファイルを見る:selected_files

ファイルを選ぶと、ブラウザ側のリストに入ります。特殊な Var のrx.selected_files(id)でその名前のリストを画面に表示し、特殊なイベントのrx.clear_selected_files(id)で空にします。まだサーバーには何も送られていません。

idはドロップゾーンと特殊な Var・イベントをつなぐ鍵なので、すべて同じ値でなければなりません。文字列かモジュールの定数で書き、State 変数は使えません。ドロップゾーン内の「Select File」ボタンは見た目だけです。ドロップゾーンの中をどこでもクリックすれば、ファイル選択ウィンドウが開くからです。

rx.vstack(
    rx.upload(
        rx.vstack(
            rx.button("Select File", variant="outline"),
            rx.text("Drag and drop files here or click to select files", size="2"),
            align="center",
        ),
        id="upload_selected",
        border="1px dotted var(--indigo-8)",
        padding="2em",
    ),
    rx.hstack(rx.foreach(rx.selected_files("upload_selected"), lambda f: rx.badge(f)), wrap="wrap"),
    rx.button("Clear", on_click=rx.clear_selected_files("upload_selected"), variant="soft", color_scheme="gray"),
)

サーバーへ送る:rx.upload_files

ハンドラーにrx.upload_files(upload_id=id)を渡すと、選んだファイルがサーバーへ送られます。ハンドラーはfiles: list[rx.UploadFile]をひとつ受け取る普通のasyncイベントです。ファイルごとにfile.name(元の名前)、file.size(バイト)、await file.read()(中身)が使えます。

アップロードのハンドラーには@rx.event(background=True)を付けず、async with selfも使いません。それらは下のチャンクアップロードでだけ使います。また、アップロードボタンはドロップゾーンの外に置きます。中に置くと、押すたびにファイルウィンドウが開きつつアップロードも始まってしまいます。

def upload_size_label(size: int) -> str:
    return f"{size / 1024:.1f} KB" if size < 1024 * 1024 else f"{size / 1024 / 1024:.2f} MB"


class UploadInfoState(rx.State):
    files: list[dict[str, str]] = []

    @rx.event
    async def handle_upload(self, files: list[rx.UploadFile]):
        # Read only the name and size. Nothing is saved to disk.
        self.files = [{"name": str(f.name), "size": upload_size_label(f.size or 0)} for f in files]


rx.vstack(
    rx.upload(
        rx.text("Drop up to 5 files (2 MB each)"),
        id="upload_info",
        max_files=5,
        max_size=2 * 1024 * 1024,
        border="2px dashed var(--gray-7)",
        padding="2em",
    ),
    rx.hstack(rx.foreach(rx.selected_files("upload_info"), lambda f: rx.badge(f, variant="soft")), wrap="wrap"),
    rx.hstack(
        rx.button("Upload", on_click=UploadInfoState.handle_upload(rx.upload_files(upload_id="upload_info"))),
        rx.button("Clear", on_click=rx.clear_selected_files("upload_info"), variant="soft", color_scheme="gray"),
    ),
    rx.foreach(
        UploadInfoState.files,
        lambda f: rx.hstack(rx.icon("file", size=14), rx.text(f["name"], size="2"), rx.code(f["size"]), align="center"),
    ),
)

保存して表示する(コードのみ)

実際のアプリでは、rx.get_upload_dir()が返すフォルダー(Path、既定は./uploaded_files、環境変数REFLEX_UPLOADED_FILES_DIRで変更可)に保存し、画面ではrx.get_upload_url(ファイル名)でアドレスを作ってrx.imageなどに渡します。下は公式ドキュメントの基本の例で、このページでは実行しません。

class State(rx.State):
    uploaded_files: list[str] = []

    @rx.event
    async def handle_upload(self, files: list[rx.UploadFile]):
        for file in files:
            data = await file.read()
            path = rx.get_upload_dir() / file.name
            with path.open("wb") as f:
                f.write(data)
            self.uploaded_files.append(file.name)


def upload_component():
    return rx.vstack(
        rx.upload(id="upload"),
        rx.button("Upload", on_click=State.handle_upload(rx.upload_files("upload"))),
        rx.foreach(
            State.uploaded_files,
            lambda f: rx.image(src=rx.get_upload_url(f), alt="Uploaded file preview"),
        ),
    )
  • rx.get_upload_dir():サーバー(バックエンド)で保存する場所。イベントハンドラーで使います。
  • rx.get_upload_url(name):画面(フロントエンド)で使うアドレス。Python の文字列ではなくフロントエンドの Var なので、コンポーネントのコードでだけ使います。アドレスは/_upload/ファイル名の形ですが、直接書かずにこの関数を通します。
  • アップロードフォルダーは/_uploadで誰にでも公開されます。同じ名前がぶつからないよう、ランダムな接頭辞を付けて保存するのがよいでしょう。
  • Reflex Cloud のようなホスティングでは、デプロイのたびにアップロードフォルダーが空になります。長く残すファイルは S3 のような外部ストレージに置きます。

一度に上げる:on_drop とフィルター

on_dropにハンドラーをつなぐと、ボタンなしで選んだ瞬間にアップロードされます。acceptでファイルの種類を、max_filesとmax_sizeで個数とサイズを絞り込みます。条件に合わないファイルはon_drop_rejectedに届き、このイベントを渡さないと 0.10 はエラーのトーストを表示します。下に、画像でも PDF でもないファイルや 2MB を超えるファイルをドロップしてみてください。

class UploadDropState(rx.State):
    names: list[str] = []
    rejected: int = 0

    @rx.event
    async def handle_drop(self, files: list[rx.UploadFile]):
        self.names = [f"{f.name} ({f.size or 0} bytes)" for f in files]

    @rx.event
    def handle_rejected(self, rejections: list):
        self.rejected = len(rejections)


rx.vstack(
    rx.upload(
        rx.text("Images or PDF only, up to 3 files", size="2"),
        id="upload_drop",
        multiple=True,
        accept={
            "application/pdf": [".pdf"],
            "image/png": [".png"],
            "image/jpeg": [".jpg", ".jpeg"],
            "image/webp": [".webp"],
        },
        max_files=3,
        max_size=2 * 1024 * 1024,
        no_keyboard=True,
        on_drop=UploadDropState.handle_drop(rx.upload_files(upload_id="upload_drop")),
        on_drop_rejected=UploadDropState.handle_rejected,
        border="2px dashed var(--gray-7)",
        padding="2em",
    ),
    rx.foreach(UploadDropState.names, lambda n: rx.text(n, size="2")),
    rx.cond(UploadDropState.rejected > 0, rx.text("Rejected: ", UploadDropState.rejected, size="2", color_scheme="red")),
)

飾りのないドロップゾーン:rx.upload.root

rx.uploadは既定の枠線と余白があるドロップゾーンです。見た目を一から自分で作りたいならrx.upload.rootを使います。プロパティとイベントは同じです。drag_active_styleで、ファイルをドラッグして入ってきている間の背景を変えられます。

rx.vstack(
    rx.upload.root(
        rx.box(
            rx.icon("cloud-upload", size=40, color=rx.color("indigo", 9)),
            rx.hstack(rx.text("Click to upload", weight="bold", color=rx.color("indigo", 11)), rx.text("or drag and drop"), size="2"),
            rx.text("SVG, PNG, JPG or GIF (MAX. 5MB)", size="1", color=rx.color("gray", 10)),
            display="flex",
            flex_direction="column",
            align_items="center",
            gap="6px",
            padding="1.5rem",
            text_align="center",
        ),
        id="upload_root_demo",
        max_width="24rem",
        border=f"2px dashed {rx.color('indigo', 7)}",
        border_radius="12px",
        cursor="pointer",
        drag_active_style={"background_color": rx.color("indigo", 3)},
    ),
    rx.hstack(rx.foreach(rx.selected_files("upload_root_demo"), lambda f: rx.badge(f)), wrap="wrap"),
)

進捗とキャンセル

rx.upload_files(とrx.upload_files_chunk)はon_upload_progressを受け取ります。アップロード中は{"loaded": 送ったバイト数, "total": 全体のバイト数, "progress": 0~1}の dict がこのハンドラーに届きます。rx.cancel_upload(id)はアップロード中の処理を止めます。このページは 2MB 以下しか受け付けないので、進捗はすぐ 100 になります。

Total bytes received: 0

class UploadProgressState(rx.State):
    uploading: bool = False
    progress: int = 0
    total_bytes: int = 0

    @rx.event
    async def handle_upload(self, files: list[rx.UploadFile]):
        self.total_bytes = sum(f.size or 0 for f in files)

    @rx.event
    def handle_upload_progress(self, progress: dict):
        self.uploading = True
        self.progress = round(progress["progress"] * 100)
        if self.progress >= 100:
            self.uploading = False

    @rx.event
    def cancel_upload(self):
        self.uploading = False
        return rx.cancel_upload("upload_progress")


rx.vstack(
    rx.upload(
        rx.text("Drag and drop files here or click to select files", size="2"),
        id="upload_progress",
        max_size=2 * 1024 * 1024,
        border="1px dotted var(--indigo-8)",
        padding="2em",
    ),
    rx.vstack(rx.foreach(rx.selected_files("upload_progress"), lambda f: rx.text(f, size="2"))),
    rx.progress(value=UploadProgressState.progress, max=100),
    rx.cond(
        ~UploadProgressState.uploading,
        rx.button(
            "Upload",
            on_click=UploadProgressState.handle_upload(
                rx.upload_files(
                    upload_id="upload_progress",
                    on_upload_progress=UploadProgressState.handle_upload_progress,
                ),
            ),
        ),
        rx.button("Cancel", on_click=UploadProgressState.cancel_upload, color_scheme="red"),
    ),
    rx.text("Total bytes received: ", UploadProgressState.total_bytes, size="2"),
    width="100%",
)

大きなファイル:チャンクアップロード(コードのみ)

await file.read()はファイル全体をメモリに載せます。大きなファイルはrx.upload_files_chunk(upload_id=...)で少しずつ受け取り、そのままディスクに書き込みます。チャンクのハンドラーは@rx.event(background=True)でなければならず、chunk_iter: rx.UploadChunkIteratorを受け取って最後まで読む必要があります(途中で戻るとアップロードが失敗します)。チャンクごとにchunk.filename、chunk.offset、chunk.content_type、chunk.dataがあります。ディスクに書き込むコードなので、ここでは実行しません。

class ChunkUploadState(rx.State):
    uploaded_files: list[str] = []

    @rx.event(background=True)
    async def handle_large_upload(self, chunk_iter: rx.UploadChunkIterator):
        file_handles = {}
        try:
            async for chunk in chunk_iter:  # must consume every chunk
                fh = file_handles.get(chunk.filename)
                if fh is None:
                    path = rx.get_upload_dir() / "stream" / chunk.filename
                    path.parent.mkdir(parents=True, exist_ok=True)
                    fh = file_handles[chunk.filename] = path.open("wb")
                fh.seek(chunk.offset)
                fh.write(chunk.data)
        finally:
            for fh in file_handles.values():
                fh.close()
        async with self:
            self.uploaded_files = sorted(file_handles)


# rx.button("Upload Large Files",
#           on_click=ChunkUploadState.handle_large_upload(rx.upload_files_chunk(upload_id="large_upload")))

フォームの中のアップロード

フォームのon_submitにはファイルは入りません。フォームの中にドロップゾーンがあるなら、ファイルはtype="button"のボタンやon_dropでrx.upload_files()を使って別に送り、残りの欄だけをon_submitで受け取ります。

注文書を仕上げる

画像の種類と個数・サイズはドロップゾーンが先に絞り込み、on_dropでドロップした瞬間にサーバーが名前とサイズを受け取ります。リストは最新の3件だけを残します。保存はしません。

Receipt photos (max 3, 2 MB each)

class UploadReceiptState(rx.State):
    files: list[dict[str, str]] = []

    @rx.event
    async def attach(self, files: list[rx.UploadFile]):
        added = [{"name": str(f.name), "size": f"{(f.size or 0) / 1024:.0f} KB"} for f in files]
        self.files = (self.files + added)[-3:]

    @rx.event
    def clear(self):
        self.files = []


rx.card(
    rx.vstack(
        rx.text("Receipt photos (max 3, 2 MB each)", weight="bold", size="2"),
        rx.upload(
            rx.hstack(rx.icon("image-plus", size=18), rx.text("Drop or click to attach", size="2"), align="center"),
            id="upload_receipt",
            accept={"image/png": [".png"], "image/jpeg": [".jpg", ".jpeg"]},
            max_files=3,
            max_size=2 * 1024 * 1024,
            on_drop=UploadReceiptState.attach(rx.upload_files(upload_id="upload_receipt")),
            border="1px dashed var(--gray-8)",
            border_radius="8px",
            padding="1em",
            width="100%",
            drag_active_style={"background_color": "var(--accent-3)"},
        ),
        rx.foreach(
            UploadReceiptState.files,
            lambda f: rx.hstack(rx.icon("image", size=14), rx.text(f["name"], size="2"), rx.spacer(), rx.code(f["size"]), width="100%", align="center"),
        ),
        rx.button("Remove all", on_click=UploadReceiptState.clear, size="1", variant="soft", color_scheme="gray"),
        width="100%",
    ),
    width="100%",
    max_width="380px",
)

工房ノート

  • rx.upload(id="固定の id") でドロップゾーン。選んだファイルは rx.selected_files(id)、空にするのは rx.clear_selected_files(id)。

  • 送信はハンドラー(rx.upload_files(upload_id=id))。ハンドラーは async で files: list[rx.UploadFile]。

  • on_drop なら選んだ瞬間にアップロード。accept · max_files · max_size で絞り込み、はじかれたものは on_drop_rejected。

  • 保存は rx.get_upload_dir()、表示は rx.get_upload_url()。アップロードフォルダーは公開される。

  • アップロードボタンはドロップゾーンの外に。大きなファイルは rx.upload_files_chunk + background ハンドラー。

← 前へ

フォーム · Form

次へ →

データ表示 · Badge

新しい授業・講座のお知らせを受け取る

この講座に授業が追加されたり、続きの講座が公開されたりしたらメールでお知らせします。

収集項目:メールアドレス・目的:この講座の新しい授業・公開のお知らせ・保有期間:解除のご依頼まで。他の目的には使わず、第三者にも提供しません。お知らせメールに返信いただければすぐに削除します。

質問と回答

この回について気になることを書き込んでください。すべての言語のページの質問がここに集まります。

リンクや書式なしで、そのままの文字で表示されます。

R

ReflexKR

触って学ぶPythonとReflex。すべての例はReflex 0.10.0で実際に動かして確認しています。

ReflexKRはバナー広告なし・無料で運営しています。役に立ったら、コーヒー一杯で応援してください。 ☕ コーヒーで応援する

アフィリエイトと支援について
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex