fix(preview): add Cache-Control header to FileResponse for browser caching (#975)

Chrome does not cache 206 Partial Content responses for <video> elements
without an explicit Cache-Control header. When VirtualScroller recycles
cards and creates new <video> elements with the same URL, Chrome
re-downloads the full video (several MB each) instead of using the cache.

Verified via Chrome DevTools: same .mp4 URL appears 2-3 times in network
trace as separate requests with no cache hit, each returning 206. With
Cache-Control: max-age=86400, the browser will reuse the cached response
for 24 hours across scroll cycles.

Video preview files are ~3.5MB while image previews are ~50-100KB (due
to WebP optimization), making caching especially impactful for videos.
This commit is contained in:
Will Miao
2026-06-14 17:36:59 +08:00
parent 6a4fd020dc
commit 818b9113f0

View File

@@ -60,7 +60,14 @@ class PreviewHandler:
# uses IOCP-based _sendfile_native which can crash when the client # uses IOCP-based _sendfile_native which can crash when the client
# disconnects mid-transfer during fast scrolling. The _stream_file() # disconnects mid-transfer during fast scrolling. The _stream_file()
# fallback is kept for a future compat toggle. # fallback is kept for a future compat toggle.
return web.FileResponse(path=resolved, chunk_size=_CHUNK_SIZE) #
# Set explicit Cache-Control so the browser can cache video (and image)
# previews across VirtualScroller recycling cycles. Without this,
# Chrome does not cache 206 Partial Content responses for <video>
# elements, causing the same video to be re-downloaded on every scroll.
resp = web.FileResponse(path=resolved, chunk_size=_CHUNK_SIZE)
resp.headers["Cache-Control"] = "public, max-age=86400"
return resp
async def _stream_file( async def _stream_file(
self, request: web.Request, path: Path self, request: web.Request, path: Path