視頻續播功能實現 - 斷點續看從前端到 Spring Boot 后端

在這里插入圖片描述

🌷 古之立大事者,不惟有超世之才,亦必有堅忍不拔之志
🎐 個人CSND主頁——Micro麥可樂的博客
🐥《Docker實操教程》專欄以最新的Centos版本為基礎進行Docker實操教程,入門到實戰
🌺《RabbitMQ》專欄19年編寫主要介紹使用JAVA開發RabbitMQ的系列教程,從基礎知識到項目實戰
🌸《設計模式》專欄以實際的生活場景為案例進行講解,讓大家對設計模式有一個更清晰的理解
🌛《開源項目》本專欄主要介紹目前熱門的開源項目,帶大家快速了解并輕松上手使用
🍎 《前端技術》專欄以實戰為主介紹日常開發中前端應用的一些功能以及技巧,均附有完整的代碼示例
?《開發技巧》本專欄包含了各種系統的設計原理以及注意事項,并分享一些日常開發的功能小技巧
💕《Jenkins實戰》專欄主要介紹Jenkins+Docker的實戰教程,讓你快速掌握項目CI/CD,是2024年最新的實戰教程
🌞《Spring Boot》專欄主要介紹我們日常工作項目中經常應用到的功能以及技巧,代碼樣例完整
👍《Spring Security》專欄中我們將逐步深入Spring Security的各個技術細節,帶你從入門到精通,全面掌握這一安全技術
如果文章能夠給大家帶來一定的幫助!歡迎關注、評論互動~

視頻續播功能實現 - 斷點續看從前端到 Spring Boot 后端

  • 1. 前言
  • 2. 為什么要做視頻續播
  • 3. 續播功能原理
      • 3.1 常見的續播記錄系統架構
      • 3.2 常見的觸發記錄時機
  • 4. 純前端實現方案
      • 4.1 基礎實現代碼
      • 4.2 增強版本地存儲
  • 5. 后端(Spring Boot)實現
      • 5.1 數據庫表
      • 5.2 后端接口
      • 5.3 Service服務及Mapper
      • 5.4. 前端調用示例
  • 6. 測試與優化
  • 7. 結語

1. 前言

在視頻網站或在線學習平臺中,用戶觀看長視頻(如課程、電影)時常會中途退出。若再次進入時不得不從頭開始,體驗大打折扣。視頻續播(Resume Playback) 功能可以幫助用戶保存上次觀看位置,下次打開時自動跳轉到該時間點繼續觀看,大幅提升用戶體驗。

比如我們常見的B站,當你播放中途退出,繼續訪問這個視頻的時候,會提示 已為您定位至XXXX 的提示,如下圖:

在這里插入圖片描述
本文博主將從為什么要做續播、續播原理、前端實現、后端實現到測試與優化,逐步拆解整個流程,并給出完整代碼示例,幫助小伙伴快速在項目中落地該功能。


2. 為什么要做視頻續播

在如今的流媒體時代,用戶平均每天觀看視頻時長超過 2.5 小時,但其中可能會出現觀看會話會被中斷(臨時退出、電話、通知、設備切換等)。能否記住播放位置并提供無縫續播體驗,已成為衡量視頻平臺專業度的重要指標!

在這里插入圖片描述

提升用戶體驗
用戶無需手動記憶上次進度,打開即看
長視頻更易于分段觀看,提高學習/觀影效率

增加平臺粘性
優質體驗能讓用戶更愿意再次回訪,延長平臺使用時長

數據價值挖掘
記錄觀看進度,可分析用戶活躍度、觀看習慣,用于個性化推薦


3. 續播功能原理

前端監聽
視頻播放進度,將當前時間點(currentTime)在用戶退出或定時時保存。

存儲進度

簡易方案:localStorage(針對單設備、單瀏覽器)
復雜方案:通過 REST 接口將進度保存到后端數據庫(支持多設備、多瀏覽器)

恢復進度
頁面加載時,讀取存儲的進度,將 <video>currentTime 設置為該值

3.1 常見的續播記錄系統架構

如上述所說,如果你僅針對單設備、單瀏覽器,可以直接使用本地存儲,但如果需要多設備支持那么就需要有如下規劃:
在這里插入圖片描述

3.2 常見的觸發記錄時機

前端在出發播放進度記錄,常見的有以下幾種

事件類型記錄策略用戶行為
暫停播放立即記錄主動暫停
離開頁面最后位置記錄關閉標簽/切換應用
播放結束重置位置完整觀看
進度拖拽延遲記錄(防抖動)快速跳轉

4. 純前端實現方案

下面給小伙伴們演示基于原生 HTML5 Video + JavaScript 的示例,使用 localStorage 做本地保存

4.1 基礎實現代碼

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>視頻續播示例</title><style>video { width: 100%; max-width: 600px; margin: 20px auto; display: block; }</style>
</head>
<body><h2>視頻續播示例</h2><!-- 視頻播放地址 --><video id="myVideo" controls><source src="https://你的視頻地址.mp4" type="video/mp4"></video><!-- 視頻播放監聽 --><script>const video = document.getElementById('myVideo');const VIDEO_ID = 'movie-123'; //視頻標識const STORAGE_KEY = `video-progress-${VIDEO_ID}`;// 初始化播放位置const savedTime = localStorage.getItem(STORAGE_KEY);if (savedTime) video.currentTime = parseFloat(savedTime);// 進度記錄函數function saveProgress() {localStorage.setItem(STORAGE_KEY, video.currentTime.toString());}// 事件監聽//拖動播放條或進度條播放變化video.addEventListener('timeupdate', throttle(saveProgress, 5000));//暫停video.addEventListener('pause', saveProgress);//播放完成video.addEventListener('ended', () => {localStorage.removeItem(STORAGE_KEY);});// 離開或刷新頁面時立即保存window.addEventListener('beforeunload', saveProgress);// 節流函數function throttle(func, delay) {let lastCall = 0;return function(...args) {const now = Date.now();if (now - lastCall >= delay) {func.apply(this, args);lastCall = now;}};}</script>
</body>
</html>

STORAGE_KEY 基于視頻 URL 唯一標識,每個視頻分開保存

4.2 增強版本地存儲

聰明的小伙伴們上述代碼案例就能看出,僅僅只能記錄并續播最后一次觀看的視頻,那么如果我希望記錄5個之前觀看中斷的視頻,那么就可以參考以下代碼:

// 存儲完整觀看記錄
function savePlaybackState() {const state = {timestamp: Date.now(),progress: video.currentTime,duration: video.duration,videoId: VIDEO_ID,percentage: (video.currentTime / video.duration * 100).toFixed(1)};// 保存最近5條記錄const history = JSON.parse(localStorage.getItem('video-history') || '[]');const newHistory = [state,...history.filter(item => item.videoId !== VIDEO_ID)].slice(0, 5);localStorage.setItem('video-history', JSON.stringify(newHistory));localStorage.setItem(STORAGE_KEY, video.currentTime.toString());
}

5. 后端(Spring Boot)實現

當需要跨設備同步或用戶登錄狀態下保存進度時,可通過后端接口存儲。下面示例用 Spring BootMyBatisMySQL 做簡易實現,供小伙伴們參考:

5.1 數據庫表

對應的實體模型小伙伴們可以自己生成

CREATE TABLE video_progress (id BIGINT AUTO_INCREMENT PRIMARY KEY,user_id BIGINT NOT NULL,video_id VARCHAR(255) NOT NULL,watched_time DOUBLE NOT NULL,update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,UNIQUE KEY idx_user_video (user_id, video_id)
);

5.2 后端接口

這里僅僅演示后端記錄視頻進度的功能,相關用戶鑒權等小伙伴們自行實現,可以參考博主的 《Spring Security》專欄進一步學習

@RestController
@RequestMapping("/api/video")
public class VideoProgressController {@Autowiredprivate VideoProgressService progressService;// 保存或更新進度@PostMapping("/progress")public ResponseEntity<?> saveProgress(@RequestBody ProgressRequest req,@RequestHeader("X-User-Id") Long userId) {progressService.saveOrUpdate(userId, req.getVideoId(), req.getCurrentTime());return ResponseEntity.ok().build();}// 獲取進度@GetMapping("/progress")public ResponseEntity<Double> getProgress(@RequestParam String videoId,@RequestHeader("X-User-Id") Long userId) {Double time = progressService.getProgress(userId, videoId);return ResponseEntity.ok(time != null ? time : 0.0);}// 請求 DTOpublic static class ProgressRequest {private String videoId;private Double currentTime;// getters/setters...}
}

5.3 Service服務及Mapper

Mapper代碼

@Mapper
public interface VideoProgressMapper {@Select("SELECT * FROM video_progress WHERE user_id=#{userId} AND video_id=#{videoId}")VideoProgress findByUserAndVideo(@Param("userId") Long userId, @Param("videoId") String videoId);@Insert("INSERT INTO video_progress(user_id,video_id,watched_time) VALUES(#{userId},#{videoId},#{watchedTime}) " +"ON DUPLICATE KEY UPDATE watched_time=#{watchedTime}, update_time=NOW()")void upsert(VideoProgress record);
}

Service代碼

@Service
public class VideoProgressService {@Autowiredprivate VideoProgressMapper mapper;public void saveOrUpdate(Long userId, String videoId, Double time) {VideoProgress record = new VideoProgress(userId, videoId, time);mapper.upsert(record);}public Double getProgress(Long userId, String videoId) {VideoProgress rec = mapper.findByUserAndVideo(userId, videoId);return rec != null ? rec.getWatchedTime() : null;}
}

5.4. 前端調用示例

<video id="myVideo" controls><source src="movie.mp4" type="video/mp4">
</video><script>
const API_BASE = '/api/video';
const video = document.getElementById('myVideo');
const VIDEO_ID = 'movie-123';
const userId = 42;  // 假設已登錄并拿到 userId
const STORAGE_KEY = `video-progress-${VIDEO_ID}`;// 恢復進度 初始化播放位置
async function loadProgress() {const res = await fetch(`${API_BASE}/progress?videoId=${videoId}`, {headers: { 'X-User-Id': userId }});const time = await res.json();if (time > 0 && time < video.duration) {video.currentTime = time;}
}// 進度記錄函數
function saveProgress() {fetch(`${API_BASE}/progress`, {method: 'POST',headers: {'Content-Type': 'application/json','X-User-Id': userId},body: JSON.stringify({ videoId, currentTime: video.currentTime })});}
}// 事件監聽
video.addEventListener('timeupdate', throttle(saveProgress, 5000));
video.addEventListener('pause', saveProgress);
video.addEventListener('ended', () => {//TODO 后端刪除API小伙伴們可自行實現
});// 頁面關閉前保存
window.addEventListener('beforeunload', saveProgress);// 節流函數
function throttle(func, delay) {let lastCall = 0;return function(...args) {const now = Date.now();if (now - lastCall >= delay) {func.apply(this, args);lastCall = now;}};
}
</script>

6. 測試與優化

測試
模擬網絡抖動、斷網重連,確保進度及時更新
跨設備登錄測試:在不同設備/瀏覽器登錄同一賬號,驗證進度同步

優化建議
數據校驗:后端對 currentTime 做合法性校驗(不超出視頻總時長)
批量提交:可改為用戶退出時一次性提交最后進度,減少請求次數
緩存 & 重試:前端調用失敗時緩存到 IndexedDB,下次自動重試
并發合并:后端可結合消息隊列異步寫庫,減小請求延遲

不同規模平臺的實施建議:
在這里插入圖片描述


7. 結語

通過本文示例,相信小伙伴已掌握了從本地存儲到后端持久化的完整視頻續播實現方案。無論是單設備場景下的 localStorage,還是支持多端同步的 Spring Boot + 數據庫方案,都能靈活應用到你的項目中。

希望這篇文章能幫助你打造更友好的視頻觀看體驗,如果你在實踐過程中有任何疑問或更好的擴展思路,歡迎在評論區留言,最后希望大家 一鍵三連 給博主一點點鼓勵!


前端技術專欄回顧:

01【前端技術】 ES6 介紹及常用語法說明
02【前端技術】標簽頁通訊localStorage、BroadcastChannel、SharedWorker的技術詳解
03 前端請求亂序問題分析與AbortController、async/await、Promise.all等解決方案
04 前端開發中深拷貝的循環引用問題:從問題復現到完美解決
05 前端AJAX請求上傳下載進度監控指南詳解與完整代碼示例
06 TypeScript 進階指南 - 使用泛型與keyof約束參數
07 前端實現視頻文件動畫幀圖片提取全攻略 - 附完整代碼樣例
08 前端函數防抖(Debounce)完整講解 - 從原理、應用到完整實現
09 JavaScript異步編程 Async/Await 使用詳解:從原理到最佳實踐
10 前端圖片裁剪上傳全流程詳解:從預覽到上傳的完整流程
11 前端大文件分片上傳詳解 - Spring Boot 后端接口實現
12 前端實現圖片防盜鏈技術詳解 - 原理分析與SpringBoot解決方案
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/86176.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/86176.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/86176.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【工具】Linux 中 find 命令使用教程

find 命令是 Linux 系統中最強大、最靈活的文件搜索工具&#xff0c;其能力遠超簡單的文件名匹配。掌握 find 能讓你在復雜的文件系統中精準定位目標&#xff0c;實現高效的文件管理。 一、命令結構與核心概念 find [起始路徑] [選項] [表達式]起始路徑&#xff1a;搜索的根目…

0629-

0629 0629操作3. 權限 0629 操作 進入數據庫 mysql -uroot -proot123 .use idatabase; select * from customer; 2.select distinct name&#xff0c;idnum from customer; 3.UPDATE customer SET idnum left(MD5(idnum),16); 4. UPDATE customer SET phone CONCAT( LEFT(p…

JVM調優實戰 Day 6:JVM性能監控工具實戰

【JVM調優實戰 Day 6】JVM性能監控工具實戰 文章簡述 在Java應用的性能優化過程中&#xff0c;JVM性能監控工具是不可或缺的“眼睛”。它們能夠幫助開發者實時掌握系統運行狀態&#xff0c;識別性能瓶頸&#xff0c;并為后續調優提供數據支撐。本文作為“JVM調優實戰”系列的第…

【嘉立創EDA】PCB 如何按板框輪廓進行鋪銅

文章路標?? :one: 文章解決問題:two: 主題內容:three: 參考方法be end..1?? 文章解決問題 操作環境:嘉立創EDA專業版 V2.2.40 本文使用嘉立創EDA,描述如何在PCB設計時,直接使用板框輪廓進行鋪銅。本文將此過程記錄,以供有需要的讀者參考。 2?? 主題內容 在PCB設計…

dockerfile命令及構建

一&#xff0c;dockerfile常用命令 命令介紹FROM–指定基礎鏡像LABEL作者信息USER切換運行屬主身份WORKDUR切換工作目錄ENV用于docker容器設置環境變量RUN用來執行命令行的命令COPY把宿主機文件復制到鏡像中去ADD將文件路徑復制添加到容器內部路徑EXPOSE為容器打開指定要監聽的…

uniApp實戰四:網絡請求封裝

文章目錄 1.最終效果預覽2.請求封裝3.創建config配置文件4.創建api請求5.頁面調用 說明&#xff1a;當前筆記基于Vue3開發&#xff0c;HbuilderX版本4.66 1.最終效果預覽 2.請求封裝 在util/request.js下創建js文件&#xff0c;代碼如下 import config from /configconst tim…

MCP協議全解:大模型時代的能力開放與服務集成最佳實踐

一、MCP協議是什么&#xff1f; MCP&#xff08;Model Context Protocol&#xff0c;模型上下文協議&#xff09;是大模型和多智能體&#xff08;Agent&#xff09;生態中&#xff0c;用于標準化描述和傳遞上下文信息、能力開放、服務集成的協議。它的目標是讓不同模型、Agent…

oracle 返回最新記錄

在Oracle數據庫中&#xff0c;如果你想獲取一個表中基于某些條件的最新記錄&#xff0c;通常有兩種常見的方法&#xff1a;使用ROWID或者使用帶有ORDER BY和ROWNUM的子查詢。下面我將介紹這兩種方法的基本用法。 方法1&#xff1a;使用ROWID 如果你的表有一個時間戳字段或者遞…

華為云服務器:Can’t connect to MySql server on ‘localhost’(10060)

本地遠程連接服務器數據庫&#xff0c;提示10060 在 Ubuntu/Debian 系統中&#xff0c;檢查 3306 端口是否開啟需要從兩個方面驗證&#xff1a;MySQL 服務是否監聽該端口以及防火墻是否允許外部訪問該端口。以下是具體步驟&#xff1a; sudo ufw status查看到為開啟mysql端口 …

利用Percona XtraDB Cluster搭建MySql高可用集群

引言拉取鏡像創建單節點實例(一般在測試環境中使用)自定義網絡(集群間相互隔離)映射數據目錄創建docker-compose PXC容器用docker-compose啟動PXC集群集群驗證數據庫負載均衡的必要性Haproxy負載均衡器部署故障排查引言 告別單點故障,擁抱持續可用——構建基于 Percona X…

Leetcode 3592. Inverse Coin Change

Leetcode 3592. Inverse Coin Change 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3592. Inverse Coin Change 1. 解題思路 這一題的話思路上我們走的是一個貪婪算法的思路&#xff0c;即從小到大依次考察&#xff0c;顯然&#xff0c;每一次當前最小的非零面額有且必有當前…

打造屬于你的AI智能體,從數據開始 —— 使用 Bright Data MCP+Trae快速構建垂直智能體

一、AI智能體的機遇與挑戰 最近這兩年全民AI熱潮開始&#xff0c;各種智能體應用層出不窮。在AI智能體火熱的當下&#xff0c;越來越多開發者想要構建自己的智能體&#xff0c;特別是垂直領域&#xff0c;需求更是旺盛。比如招聘助手、電商導購、財經分析師等等。從技術角度來…

嵌入式自學四十八天

時鐘 cpu528MHz&#xff0c; PLL&#xff1a;鎖相環電路 倍頻功能&#xff1a;Fin*n Fout Prescale&#xff1a; 預分頻器 降頻 Fin/m Fout PFD&#xff1a;相位分子分頻器 Fin *n/m Fout 時鐘開了后&#xff0c;先到時鐘根產生器&#xff0c;對時鐘頻率更改&…

光譜相機應對復雜環境條件的關鍵技術與方案

一、極端溫度適應性? ?主動溫控系統? ?半導體冷卻&#xff08;TEC&#xff09;?&#xff1a;維持探測器在5-40℃工作區間&#xff0c;防止高溫噪聲&#xff08;如SPECIM FX17&#xff09;。 ?散熱結構?&#xff1a;鋁合金外殼散熱鰭片&#xff0c;工業級相機可在-10℃…

個人技術文檔庫構建實踐:基于Cursor和GitHub的知識管理系統(含cursor rules)

技術選型 核心工具鏈 Cursor編輯器&#xff1a;AI輔助寫作&#xff0c;智能補全和結構優化GitHub&#xff1a;版本控制、跨設備同步、團隊協作Markdown&#xff1a;輕量級格式&#xff0c;跨平臺兼容&#xff0c;與Git完美集成 與主流工具對比 選擇CursorGitHub適合&#xf…

煙花爆竹生產企業庫房存儲安全風險預警系統

煙花爆竹生產企業庫房存儲安全風險預警系統是保障庫房物資安全、規范作業流程、防范安全事故的重要技術手段&#xff0c;涵蓋多個關鍵預警功能。? 溫濕度預警? 在庫房內安裝溫濕度傳感器&#xff0c;這些傳感器如同敏銳的“環境感知員”&#xff0c;能夠實時監測庫房內環境變…

LINUX 625 DNS域名管理系統

建安錯題 根據《安全色》&#xff0c;紅、黃、藍、綠四種安全色各自傳遞著不同的安全含義和信息,其中表示要求人們必須遵守的規定的顏色是()。 根據《安全色》國家標準&#xff08;GB 2893-2008&#xff09;&#xff0c;四種安全色的含義如下&#xff1a; ??紅色??&#…

FastMCP框架進行MCP開發:(三)從SSE升級到SteamableHTTP

一、前言 在MCP&#xff08;Model Context Protocol&#xff09;中&#xff0c;Streamable HTTP和SSE&#xff08;Server-Sent Events&#xff09;都是用于實現客戶端與服務器之間通信的傳輸機制。然而&#xff0c;它們在設計、功能以及性能表現上有著顯著的區別。 二、SSE在…

Android 15 變更及適配攻略

2025年的第一篇Android適配&#xff0c;比以往來的更晚一些。廢話不多說&#xff0c;我們開始&#xff01;&#xff01; 準備工作 首先將我們項目中的 targetSdk和compileSdk 升至 35。 推薦使用Android Studio Koala Feature Drop | 2024.1.2或更高版本。AGP版本最低升級到…

Vue項目使用defer優化頁面白屏,性能優化提升,秒加載!!!

defer表示延遲加載&#xff0c;針對大量節點的渲染加載&#xff0c;結合使用關鍵幀requestAnimationFrame的形式來分片加載&#xff0c;可以優化白屏時間 知識補充&#xff1a; requestAnimationFrame requestAnimationFrame 是根據幀數來執行回調函數的&#xff0c;就是屏幕…