如何在不重新加载的情况下更换HTML5 video的src?含S3私有视频场景
1. Changing HTML5 Video src Without Full Page Reload (and Preserving Playback State)
First, a quick clarification: updating the src attribute won’t reload your entire page, but it will trigger the video element to load the new source. The "problem" you’re noticing (the video acting like it’s a new file) usually comes from losing playback position or interrupting the viewing experience. Here’s how to do it smoothly:
- Preserve playback state before updating the source:
Save the current playback time and playback status, swap thesrc, then restore the state once the new source is ready.
const video = document.getElementById('your-video-id'); async function updateVideoSrc(newUrl) { // Save current state const currentTime = video.currentTime; const wasPlaying = !video.paused; // Update the source video.src = newUrl; // Wait for the new source to load metadata await new Promise(resolve => video.addEventListener('loadedmetadata', resolve, { once: true })); // Restore playback video.currentTime = currentTime; if (wasPlaying) { video.play().catch(err => console.error('Error resuming playback:', err)); } }
- Alternative for
<source>elements:
If you’re using<source>tags inside your video element, update the source’ssrcand callvideo.load(), following the same state-preservation steps:
<video id="my-video" controls> <source id="video-source" src="old-url" type="video/mp4"> </video>
const source = document.getElementById('video-source'); const video = document.getElementById('my-video'); async function updateSource(newUrl) { const currentTime = video.currentTime; const wasPlaying = !video.paused; source.src = newUrl; video.load(); await new Promise(resolve => video.addEventListener('loadedmetadata', resolve, { once: true })); video.currentTime = currentTime; if (wasPlaying) video.play(); }
2. Playing Private Amazon S3 Videos with Pre-Signed URLs
Using pre-signed URLs is absolutely the right approach for this scenario—it’s the standard way to grant temporary access to private S3 objects. To fix the expired URL issue and make playback seamless, try these strategies:
Smoothly Handle Expired URLs
The video element fires an error event when it can’t load the source (like when a pre-signed URL expires). Use this to trigger a refresh:
// Fetch a new pre-signed URL from your backend (replace with your API endpoint) async function getNewSignedUrl() { const response = await fetch('/api/generate-s3-video-url'); const data = await response.json(); return data.signedUrl; } // Listen for video load errors video.addEventListener('error', async (e) => { const error = e.target.error; // Check if the error is due to forbidden access (expired URL) if (error.code === MediaError.MEDIA_ERR_NETWORK && video.src.includes('s3.amazonaws.com')) { console.log('Pre-signed URL expired—refreshing...'); try { const newUrl = await getNewSignedUrl(); await updateVideoSrc(newUrl); // Use the state-preserving function from earlier } catch (err) { console.error('Failed to refresh URL:', err); // Show a user-friendly error message here } } });
Proactively Refresh URLs Before Expiry
Even better than waiting for an error: if your backend returns the expiry time along with the pre-signed URL, set a timer to refresh the URL a few seconds before it expires. This eliminates interruptions entirely:
async function loadVideoWithAutoRefresh() { const { signedUrl, expiresAt } = await fetch('/api/generate-s3-video-url'); video.src = signedUrl; // Calculate time to refresh (10 seconds before expiry) const refreshTime = new Date(expiresAt) - Date.now() - 10000; if (refreshTime > 0) { setTimeout(async () => { console.log('Refreshing URL before expiry...'); await updateVideoSrc(await getNewSignedUrl()); }, refreshTime); } } // Initialize the video loadVideoWithAutoRefresh();
Better Alternatives to Pre-Signed URLs?
Pre-signed URLs are the simplest, most cost-effective solution for most cases. But if you need more control:
- CloudFront Signed URLs/Cookies: If you use CloudFront to serve S3 videos, signed URLs/cookies offer tighter access controls (like IP restrictions) and better performance via CDN. The workflow is similar to S3 pre-signed URLs.
- Media Server with Token Auth: For DRM-protected or large-scale content, use a media server (like AWS Elemental MediaConvert) that handles token-based auth. The server fetches the video from S3 behind the scenes, so users never access S3 directly.
内容的提问来源于stack exchange,提问作者William Rosenbloom

