Skip to content

Commit 9852382

Browse files
LineIndentAlek99
andauthored
[ENG-5988] additions to upload docs (#1389)
* additions to upload docs * changes to docs * +changes --------- Co-authored-by: Alek Petuskey <[email protected]>
1 parent 0ee7c31 commit 9852382

File tree

1 file changed

+184
-10
lines changed

1 file changed

+184
-10
lines changed

docs/library/forms/upload.md

Lines changed: 184 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,141 @@
11
---
22
components:
33
- rx.upload
4+
- rx.upload.root
45

56
Upload: |
6-
lambda **props: rx.center(rx.upload(id="my_upload", **props), height="4em", width="100%")
7+
lambda **props: rx.center(rx.upload(id="my_upload", **props))
8+
lambda **props: rx.center(rx.upload(id="my_upload", **props))
79
---
810

911
```python exec
1012
import reflex as rx
1113
```
1214

13-
# Upload
15+
# File Upload
1416

15-
The Upload component can be used to upload files to the server.
17+
Reflex makes it simple to add file upload functionality to your app. You can let users select files, store them on your server, and display or process them as needed. Below is a minimal example that demonstrates how to upload files, save them to disk, and display uploaded images using application state.
1618

17-
You can pass components as children to customize its appearance.
18-
You can upload files by clicking on the component or by dragging and dropping files onto it.
1919

20-
```python demo
21-
rx.upload(
22-
id="my_upload",
23-
)
20+
## Basic File Upload Example
21+
22+
You can let users upload files and keep track of them in your app’s state. The example below allows users to upload files, saves them using the backend, and then displays the uploaded files as images.
23+
24+
```python
25+
import reflex as rx
26+
class State(rx.State):
27+
uploaded_files: list[str] = []
28+
29+
@rx.event
30+
async def handle_upload(self, files: list[rx.UploadFile]):
31+
for file in files:
32+
data = await file.read()
33+
path = rx.get_upload_dir() / file.name
34+
with path.open("wb") as f:
35+
f.write(data)
36+
self.uploaded_files.append(file.name)
37+
38+
def upload_component():
39+
return rx.vstack(
40+
rx.upload(id="upload"),
41+
rx.button("Upload", on_click=State.handle_upload(rx.upload_files("upload"))),
42+
rx.foreach(State.uploaded_files, lambda f: rx.image(src=rx.get_upload_url(f))),
43+
)
2444
```
2545

26-
Selecting a file will add it to the browser's file list, which can be rendered
46+
## How File Upload Works
47+
48+
Selecting a file will add it to the browser file list, which can be rendered
2749
on the frontend using the `rx.selected_files(id)` special Var. To clear the
2850
selected files, you can use another special Var `rx.clear_selected_files(id)` as
2951
an event handler.
3052

3153
To upload the file(s), you need to bind an event handler and pass the special
3254
`rx.upload_files(upload_id=id)` event arg to it.
3355

56+
## File Storage Functions
57+
58+
Reflex provides two key functions for handling uploaded files:
59+
60+
### rx.get_upload_dir()
61+
- **Purpose**: Returns a `Path` object pointing to the server-side directory where uploaded files should be saved
62+
- **Usage**: Used in backend event handlers to determine where to save uploaded files
63+
- **Default Location**: `./uploaded_files` (can be customized via `REFLEX_UPLOADED_FILES_DIR` environment variable)
64+
- **Type**: Returns `pathlib.Path`
65+
66+
### rx.get_upload_url(filename)
67+
- **Purpose**: Returns the URL string that can be used in frontend components to access uploaded files
68+
- **Usage**: Used in frontend components (like `rx.image`, `rx.video`) to display uploaded files
69+
- **URL Format**: `/_upload/filename`
70+
- **Type**: Returns `str`
71+
72+
### Key Differences
73+
- **rx.get_upload_dir()** -> Backend file path for saving files
74+
- **rx.get_upload_url()** -> Frontend URL for displaying files
75+
76+
### Basic Upload Pattern
77+
78+
Here is the standard pattern for handling file uploads:
79+
80+
```python
81+
import reflex as rx
82+
83+
def create_unique_filename(file_name: str):
84+
import random
85+
import string
86+
87+
filename = "".join(random.choices(string.ascii_letters + string.digits, k=10))
88+
return filename + "_" + file_name
89+
90+
class State(rx.State):
91+
uploaded_files: list[str] = []
92+
93+
@rx.event
94+
async def handle_upload(self, files: list[rx.UploadFile]):
95+
"""Handle file upload with proper directory management."""
96+
for file in files:
97+
# Read the file data
98+
upload_data = await file.read()
99+
100+
# Get the upload directory (backend path)
101+
upload_dir = rx.get_upload_dir()
102+
103+
# Ensure the directory exists
104+
upload_dir.mkdir(parents=True, exist_ok=True)
105+
106+
# Create unique filename to prevent conflicts
107+
unique_filename = create_unique_filename(file.name)
108+
109+
# Create full file path
110+
file_path = upload_dir / unique_filename
111+
112+
# Save the file
113+
with file_path.open("wb") as f:
114+
f.write(upload_data)
115+
116+
# Store filename for frontend display
117+
self.uploaded_files.append(unique_filename)
118+
119+
def upload_component():
120+
return rx.vstack(
121+
rx.upload(
122+
rx.text("Drop files here or click to select"),
123+
id="file_upload",
124+
border="2px dashed #ccc",
125+
padding="2em",
126+
),
127+
rx.button(
128+
"Upload Files",
129+
on_click=State.handle_upload(rx.upload_files(upload_id="file_upload")),
130+
),
131+
# Display uploaded files using rx.get_upload_url()
132+
rx.foreach(
133+
State.uploaded_files,
134+
lambda filename: rx.image(src=rx.get_upload_url(filename))
135+
),
136+
)
137+
138+
```
34139

35140
### Multiple File Upload
36141

@@ -250,6 +355,57 @@ def index():
250355
)
251356
```
252357

358+
### Unstyled Upload Component
359+
360+
To use a completely unstyled upload component and apply your own customization, use `rx.upload.root` instead:
361+
362+
```python demo
363+
rx.upload.root(
364+
rx.box(
365+
rx.icon(
366+
tag="cloud_upload",
367+
style={"width": "3rem", "height": "3rem", "color": "#2563eb", "marginBottom": "0.75rem"},
368+
),
369+
rx.hstack(
370+
rx.text(
371+
"Click to upload",
372+
style={"fontWeight": "bold", "color": "#1d4ed8"},
373+
),
374+
" or drag and drop",
375+
style={"fontSize": "0.875rem", "color": "#4b5563"},
376+
),
377+
rx.text(
378+
"SVG, PNG, JPG or GIF (MAX. 5MB)",
379+
style={"fontSize": "0.75rem", "color": "#6b7280", "marginTop": "0.25rem"},
380+
),
381+
style={
382+
"display": "flex",
383+
"flexDirection": "column",
384+
"alignItems": "center",
385+
"justifyContent": "center",
386+
"padding": "1.5rem",
387+
"textAlign": "center",
388+
},
389+
),
390+
id="my_upload",
391+
style={
392+
"maxWidth": "24rem",
393+
"height": "16rem",
394+
"borderWidth": "2px",
395+
"borderStyle": "dashed",
396+
"borderColor": "#60a5fa",
397+
"borderRadius": "0.75rem",
398+
"cursor": "pointer",
399+
"transitionProperty": "background-color",
400+
"transitionDuration": "0.2s",
401+
"transitionTimingFunction": "ease-in-out",
402+
"display": "flex",
403+
"alignItems": "center",
404+
"justifyContent": "center",
405+
"boxShadow": "0 1px 2px rgba(0, 0, 0, 0.05)",
406+
},
407+
)
408+
```
253409

254410
## Handling the Upload
255411

@@ -271,6 +427,24 @@ The backend of your app will mount this uploaded files directory on `/_upload` w
271427
# When using the Reflex hosting service, the uploaded files directory is not persistent and will be cleared on every deployment. For persistent storage of uploaded files, it is recommended to use an external service, such as S3.
272428
```
273429

430+
### Directory Structure and URLs
431+
432+
By default, Reflex creates the following structure:
433+
434+
```text
435+
your_project/
436+
├── uploaded_files/ # rx.get_upload_dir() points here
437+
│ ├── image1.png
438+
│ ├── document.pdf
439+
│ └── video.mp4
440+
└── ...
441+
```
442+
443+
The files are automatically served at:
444+
- `/_upload/image1.png``rx.get_upload_url("image1.png")`
445+
- `/_upload/document.pdf``rx.get_upload_url("document.pdf")`
446+
- `/_upload/video.mp4``rx.get_upload_url("video.mp4")`
447+
274448
## Cancellation
275449

276450
The `id` provided to the `rx.upload` component can be passed to the special event handler `rx.cancel_upload(id)` to stop uploading on demand. Cancellation can be triggered directly by a frontend event trigger, or it can be returned from a backend event handler.

0 commit comments

Comments
 (0)