Repository navigation
fix(files): stream served files with byte ranges instead of buffering them under 100 MB - #9542
Conversation
… them under 100 MB
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
[High impact] The PR appears safe to merge; no new actionable issues were found. SummaryFiles served without changes now stream from storage with byte-range support. Rendered pages, generated documents, and image previews keep their bounded, buffered paths.
|
…s, prove ranges on real bytes
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 23 files
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
…uments in-page, version local objects by inode
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 24 files
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
Summary
/api/files/serveread every file fully into memory and refused anything over 100 MB (413), while workspace files are admitted up to 5 GB, so large files (e.g. agent-rendered videos) could never be downloaded or previewedpreview=1, which may be a HEIC transcode) now streams from storage instead; transformed representations stay buffered and bounded exactly as beforeresolveByteRange(single range, suffix, clamp, 416,If-Rangestrong-ETag only), rangeddownloadFileStreamfor S3 / Blob / GCS / local, andcreateStoredFileStreamResponse(200 / 206 / 304 / 416 withAccept-Ranges,Content-Range, a strongETag). Seeking works, and Safari can play at allstreamAuthorizedWorkspaceFilehelper (access + read receipt + provenance rechecked after storage opens, stream disposed on refusal); the inline-image use case now uses the same helper instead of its own copyreadWorkspaceFileContentByKeyuse case (raw reads now stream) and renamed its module toread-workspace-file-record-by-keyservesByteRanges; project and public-share sources keep the blob path. A failed load is diagnosed with a HEAD probe (replaced key → record re-resolved; unplayable format → "can't be played" message), and a load that finishes before hydration is picked up on mount so the spinner never sticksType of Change
Testing
lib/uploads/server/byte-range.test.ts(29 cases, written failure-modes-first),stream-authorized-workspace-file.test.ts, streaming cases inapp/api/files/serve/[...path]/route.test.ts(200/206/416/304/If-Range, legacy streaming, buffered paths unchanged), large-download cases inlib/uploads/client/download.test.ts; each guard red-checked by reverting itcheck:api-validation:strict,check:comment-hygiene,check:source-conventions,check:client-boundary,check:test-patterns,check:file-names,check:utils,check:boundaries, block registry check, lintnext devwith local storage and a real session: 14/14 HTTP checks on a 151 MB non-faststart MP4 (byte-exact full download by SHA-256, exact 206 slices incl. suffix/tail, 416, 304, stale/matching If-Range, HEAD, immutablev=, unauthenticated 401); WebKit + Chromium: preview plays, seeks to near the end, Download button saves the exact file; a 5 MB clip behaves the same; server RSS flat while a 1 MB/s client streamed the whole filebun run test,check:auditsandtype-checkleft to CIChecklist
test-auditauthoring gate)🤖 Generated with Claude Code