feat: add chunked file upload support Streaming Upload API (rx.upload_files_chunk)#6190
feat: add chunked file upload support Streaming Upload API (rx.upload_files_chunk)#6190FarhanAliRaza wants to merge 12 commits intoreflex-dev:mainfrom
Conversation
…_files_chunk) Implement chunked/streaming file uploads to handle large files without loading them entirely into memory. Moves upload handling logic from app.py to event.py, adds chunked upload JS helpers, and updates the upload component to support the new upload_files_chunk API. Includes unit and integration tests for chunked upload, cancel, and streaming.
Merging this PR will improve performance by 3.48%
Performance Changes
Comparing |
Greptile SummaryThis PR implements chunked/streaming file uploads via a new How it fits in: Both the existing buffered path ( Key changes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Browser
participant upload.js
participant state.js
participant UploadEndpoint as /upload (server)
participant _upload.py
participant BackgroundTask
participant UploadChunkIterator
participant StateProxy
Browser->>state.js: user clicks Upload (uploadFilesChunk event)
state.js->>state.js: applyRestEvent (handler === "uploadFiles")
state.js->>upload.js: uploadFiles(handler, files, upload_id, ...)
upload.js->>UploadEndpoint: POST /upload (multipart, Reflex-Event-Handler header)
UploadEndpoint->>_upload.py: upload_file(request)
_upload.py->>_upload.py: _require_upload_headers(request)
_upload.py->>_upload.py: _get_upload_runtime_handler(app, token, handler)
_upload.py->>_upload.py: handler.is_background → resolve_upload_chunk_handler_param
_upload.py->>UploadChunkIterator: UploadChunkIterator(maxsize=8)
_upload.py->>BackgroundTask: app._process_background(state, event{chunk_iter})
BackgroundTask-->>_upload.py: asyncio.Task
_upload.py->>UploadChunkIterator: set_consumer_task(task)
activate BackgroundTask
BackgroundTask->>UploadChunkIterator: async for chunk in chunk_iter (blocks waiting)
_upload.py->>_upload.py: _UploadChunkMultipartParser.parse()
loop Each network chunk from stream
_upload.py->>UploadChunkIterator: push(UploadChunk)
UploadChunkIterator->>BackgroundTask: yields chunk (wakes consumer)
BackgroundTask->>StateProxy: async with self (update progress state)
StateProxy-->>Browser: state delta via WebSocket
end
_upload.py->>UploadChunkIterator: finish()
BackgroundTask->>BackgroundTask: handler returns (files written)
BackgroundTask->>StateProxy: async with self (set completed_files)
StateProxy-->>Browser: final state delta via WebSocket
deactivate BackgroundTask
_upload.py-->>upload.js: 202 + StateUpdate(final=True) ndjson
upload.js-->>Browser: upload complete
Reviews (2): Last reviewed commit: "refactor: move UploadChunk exports from ..." | Re-trigger Greptile |
masenf
left a comment
There was a problem hiding this comment.
if possible, the frontend code should be consolidated. i don't think there's a need to change the frontend code at all. you should be able to detect which type of upload is used in the backend and dispatch to the correct upload type based on the resolved handler arg type
Move upload helpers from reflex/upload.py to reflex/_upload.py, unify the frontend to use a single uploadFiles function instead of separate uploadFiles/uploadFilesChunk paths, and normalize upload payload keys server-side in state.py instead of branching in the JS client.
|
Don't know why pre-commit is failing. |
The pyi_generator script created files that different from the last known hash. i.e. "something" in the pyi output changed and it looks like it was something that most components are inheriting, so probably in the base component class EDIT: actually i see that you added UploadFile as default import, so every pyi file got its hash changed. do we need UploadFile as a default import? |
…hrough upload endpoint Move UploadChunk and UploadChunkIterator from reflex.event to reflex._upload, use lazy imports to break circular dependencies, and remove early-return guards for empty file lists. Empty uploads now flow through the normal upload path instead of being short-circuited on the frontend or normalized via websocket fallback (_normalize_upload_payload removed). Adds tests for empty buffered and chunk uploads with aliased handler parameters.
Re-export UploadChunk and UploadChunkIterator directly from reflex._upload instead of re-importing them through reflex.event, removing the eager import_module call at module load time.
|
@greptile-apps please do a final re-review |

Implement chunked/streaming file uploads to handle large files without loading them entirely into memory. Moves upload handling logic from app.py to event.py, adds chunked upload JS helpers, and updates the upload component to support the new upload_files_chunk API. Includes unit and integration tests for chunked upload, cancel, and streaming.
All Submissions:
Type of change
Please delete options that are not relevant.
New Feature Submission:
Changes To Core Features:
closes #6184