mirror of
https://github.com/open-webui/open-webui.git
synced 2026-05-06 10:58:17 -05:00
[PR #24023] [CLOSED] feat: chunked file uploads on iOS / iPadOS #43110
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/24023
Author: @omal1ar0v
Created: 4/23/2026
Status: ❌ Closed
Base:
dev← Head:feat/chunked-upload-ios📝 Commits (10+)
fe6783cMerge pull request #19030 from open-webui/devfc05e0aMerge pull request #19405 from open-webui/deve3faec6Merge pull request #19416 from open-webui/dev9899293Merge pull request #19448 from open-webui/dev140605eMerge pull request #19462 from open-webui/dev6f1486fMerge pull request #19466 from open-webui/devd95f533Merge pull request #19729 from open-webui/deva7271530.6.43 (#20093)6adde20Merge pull request #20394 from open-webui/devf9b0534Merge pull request #20522 from open-webui/dev📊 Changes
4 files changed (+533 additions, -5 deletions)
View changed files
📝
backend/open_webui/routers/files.py(+340 -0)📝
src/lib/apis/files/index.ts(+159 -4)📝
src/lib/components/chat/MessageInput.svelte(+22 -1)📝
src/lib/utils/index.ts(+12 -0)📄 Description
Pull Request Checklist
Before submitting, make sure you've checked the following:
dev.(
CHUNKED_UPLOAD_MAX_SIZE_MB,CHUNKED_UPLOAD_MAX_SIZE_FALLBACK_MB,CHUNKED_UPLOAD_SESSION_TTL_SECONDS) once thisis reviewed.
large image produced "Load failed" before the fix. After the fix, the same file uploads cleanly. Also verified on
desktop that the existing single-POST path is unchanged (iOS gate is
false). Additional end-to-endcurltest ofthe chunked endpoints included in the test plan below.
reviewed line-by-line by the human author, and the full feature was manually tested end-to-end on a real iOS device
(iPhone, Safari-based home-screen WebClip) before being submitted.
change and no new UI surface. Max-size config reuses the existing
RAG_FILE_MAX_SIZErather than introducing a newuser-facing setting.
dev.feat: …Changelog Entry
Description
Adds a chunked upload path gated on iOS / iPadOS WebKit to work around its body-size and
FileReader/<canvas>memory limits. Desktop behaviour is unchanged.
iOS/iPadOS WebKit silently aborts large multipart POSTs with a generic "Load failed", and
FileReader+<canvas>onthose platforms have hard memory ceilings that crash large images before the upload can even start. The net effect:
iPhone / iPad users cannot attach larger images or files in a chat, even when both the model and the admin config
allow it.
This PR introduces a parallel chunked-upload code path that only runs when
isIOSLike()returns true; every otherplatform keeps the existing single-POST behaviour byte-for-byte. Chunk / session state is kept small and disposable,
and the per-upload size cap piggy-backs on the existing global
RAG_FILE_MAX_SIZEso admins don't have to configuretwo limits.
Added
/api/v1/files/chunked/:POST /start— returns anupload_id, creates a per-session temp dir bound to the calling user.POST /{upload_id}— appends raw bytes at a givenoffset(verified server-side to catch drift).POST /{upload_id}/complete— wraps the assembled temp file and hands it to the existingupload_file_handler,so storage / DB / processing behaviour is shared with non-chunked uploads.
DELETE /{upload_id}— abort & clean up.uploadFile(): whenisIOSLike() && size > 500 KiB, slice into 500 KiB pieces and send themsequentially; otherwise behave exactly as before.
MessageInput.svelteiOS fast-path: skips theFileReader+<canvas>pipeline for image attachments when nocompression / HEIC conversion / temporary-chat inlining is required — that pipeline was the actual failure point for
large images on iOS, well before the upload fetch was issued.
isIOSLike()helper insrc/lib/utils/index.ts, handling iPadOS-in-desktop-UA-mode vianavigator.maxTouchPoints.CHUNKED_UPLOAD_MAX_SIZE_MB— optional override for the chunked path's size cap.CHUNKED_UPLOAD_MAX_SIZE_FALLBACK_MB— safety fallback when neither the override norRAG_FILE_MAX_SIZEis set(default 5 GiB).
CHUNKED_UPLOAD_SESSION_TTL_SECONDS— TTL for the best-effort reaper that sweeps abandoned chunk sessions(default 24h).
CHUNKED_UPLOAD_MAX_SIZE_MB→FILE_MAX_SIZE(RAG_FILE_MAX_SIZE) →CHUNKED_UPLOAD_MAX_SIZE_FALLBACK_MB.Changed
Deprecated
Removed
Fixed
configured
RAG_FILE_MAX_SIZEbut exceeded WebKit's effective per-request body limit.FileReader.readAsDataURL()/<canvas>before the upload couldstart, even when no compression was needed.
Security
403.upload_idpath segments are validated (reject..,/,\) to prevent traversal out of the chunk directory.409with the server'sauthoritative
receivedcount.sizeis capped to the effective max (see config chain above) and enforced again on each chunk to refusepayloads exceeding the declaration.
Breaking Changes
Additional Information
AI disclosure: This PR was written with an AI coding assistant (Claude Opus). The AI produced the initial drafts
of the backend endpoints, the frontend chunking logic, and the iOS detection helper. Every line was then
human-reviewed before commit; the design decisions (chunk size, iOS-only gating, reuse of
RAG_FILE_MAX_SIZE, session cleanup strategy) were human choices. The feature was manually tested on a real iPhone against a locally-built Dockerimage — the original "Load failed" bug was reproduced before the fix, and the fix was verified to resolve it. The
backend was also validated with the
curlscript shown below.Test plan (manual, reproducible):
POST /api/v1/files/(existing code path). ✅POST /chunked/start, a sequence ofPOST /chunked/{id}?offset=..., thenPOST /chunked/{id}/complete. File appearsattached and can be sent to the model. ✅
curl(not iOS-gated; backend only):gone from UPLOAD_DIR/.chunks/.
4. Negative: send a second chunk with a wrong offset — backend responds 409 with the authoritative received count,
frontend aborts the session. ✅
5. Negative: start a session, walk away — after CHUNKED_UPLOAD_SESSION_TTL_SECONDS, the next /chunked/start call by
any user reaps it. ✅
Design notes:
much lower than the historical "50 MB" figure. More round-trips but no failures.
chunk size.
Screenshots or Videos
/chunked/?offset=... → /chunked//complete]
Contributor License Agreement
https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT, and I am providing my contributions
under its terms.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.