跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

视频续播功能实现 - 断点续看从前端到 Spring Boot 后端

视频续播功能实现,涵盖前端监听进度、本地存储及后端 Spring Boot 持久化方案。通过对比单设备与多设备架构,提供数据库设计、接口定义及完整代码示例。包含节流防抖优化建议,解决用户中断观看后自动恢复进度问题,提升用户体验。

DockerOne发布于 2026/4/5更新于 2026/7/2363 浏览
视频续播功能实现 - 断点续看从前端到 Spring Boot 后端

在这里插入图片描述

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 Boot+MyBatis+MySQL 做简易实现,供小伙伴们参考:

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 后端接口

这里仅仅演示后端记录视频进度的功能,相关用户鉴权等小伙伴们自行实现,可以参考相关安全框架文档进一步学习

@RestController
@RequestMapping("/api/video")
public class VideoProgressController {
    @Autowired
    private 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);
    }

    // 请求 DTO
    public 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 {
    @Autowired
    private 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 + 数据库 方案,都能灵活应用到你的项目中。

希望这篇文章能帮助你打造更友好的视频观看体验,如果你在实践过程中有任何疑问或更好的扩展思路,欢迎在评论区留言交流。

目录

  1. 1. 前言
  2. 2. 为什么要做视频续播
  3. 3. 续播功能原理
  4. 3.1 常见的续播记录系统架构
  5. 3.2 常见的触发记录时机
  6. 4. 纯前端实现方案
  7. 4.1 基础实现代码
  8. 4.2 增强版本地存储
  9. 5. 后端(Spring Boot)实现
  10. 5.1 数据库表
  11. 5.2 后端接口
  12. 5.3 Service 服务及 Mapper
  13. 5.4. 前端调用示例
  14. 6. 测试与优化
  15. 7. 结语
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 轻量级环境管理工具 UV 入门与 Windows 安装实战
  • VSCode 中 GitHub Copilot 的安装配置与高效用法
  • 大模型微调技术深度解析与实践
  • Redis Hash 与 List 实战:数据结构、指令与场景优化
  • Spring Boot 虚拟线程时代:WebFlux 与 WebMVC 选型指南
  • 阿里云 MoltBot 机器人钉钉 Stream 流式接入配置
  • Python 魔法方法与数据模型深度解析
  • 飞算 Java AI 实战指南:从环境配置到项目生成
  • Python 打造 AI 三剑客:文档总结、代码生成与智能检索
  • C++ STL String 类常用接口说明及相关练习
  • Python 在数据化运营中的应用与价值解析
  • AI Agent 实战:核心架构与生产级落地指南
  • Xilinx 7 系列 FPGA 收发器架构与时钟设计
  • OpenClaw 开源汉化版安装与配置指南
  • HarmonyOS PreviewKit 文件预览功能入门与后端示例
  • OpenClaw.ai:Agentic AI 时代的 Spring Framework 时刻
  • 阿里巴巴低代码引擎实战:构建企业级可视化开发平台
  • 大模型蒸馏有多普遍:一项量化研究的发现
  • 结构化信息管理与 ChatGPT 的高效应用
  • Git 与 TortoiseGit 详细安装及使用指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online