You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django+Video.js视频在Chrome中Seek功能失效问题求助

解决Chrome中Video.js播放S3视频时Seek功能失效的问题

问题根源

Chrome浏览器的视频播放对HTTP Range请求有硬性依赖:当用户点击进度条或使用方向键seek时,浏览器会发送带Range头的请求,要求服务器返回指定区间的视频数据。你的Django后端当前直接返回整个视频文件,既不处理Range请求,也不返回Accept-Ranges响应头,导致Chrome无法准确定位视频位置,只能从头播放。其他浏览器对无Range支持的场景做了降级处理,因此能正常工作。


一、修复Django后端,支持Range请求

修改GenericFileOperation的get方法,添加Range请求处理逻辑:

class GenericFileOperation(APIView):
    def get(self, request, material_type=None, course_id=None, format=None):
        key = request.GET.get('key')
        download = request.GET.get('download')

        if key is not None:
            s3_obj = download_assessment_file(key)
            splited_path = key.split("/")
            file_name = splited_path[-1]
            file_size = s3_obj['ContentLength']
            mime_type, _ = mimetypes.guess_type(key)

            # 处理Range请求(跳过下载场景)
            range_header = request.META.get('HTTP_RANGE', '')
            if range_header and not download:
                # 解析Range头,格式示例:"bytes=0-1023"
                range_part = range_header.split('=')[-1]
                start_str, end_str = range_part.split('-')
                start = int(start_str) if start_str else 0
                end = int(end_str) if end_str else file_size - 1
                end = min(end, file_size - 1)
                
                # 从S3获取指定范围的内容
                s3_range = f'bytes={start}-{end}'
                s3_obj_range = download_assessment_file(key, range=s3_range)
                content = s3_obj_range['Body'].read()
                content_length = end - start + 1

                response = HttpResponse(content, content_type=mime_type, status=206)
                response['Content-Range'] = f'bytes {start}-{end}/{file_size}'
                response['Accept-Ranges'] = 'bytes'
                response['Content-Length'] = str(content_length)
            else:
                # 返回完整文件或处理下载请求
                contents = s3_obj['Body'].read()
                temp = tempfile.TemporaryFile()
                temp.write(contents)
                temp.seek(0)
                response = HttpResponse(temp, content_type=mime_type)
                if download is not None:
                    response['Content-Disposition'] = f"attachment; filename={file_name}"
                response['Accept-Ranges'] = 'bytes'

            response['X-Frame-Options'] = "*"
            return response

注意:需确保download_assessment_file函数支持传递range参数调用S3的GetObject API,S3原生支持通过Range头获取文件片段。


二、优化React端Video.js代码,避免配置冲突

当前代码用innerHTML创建video元素,同时传递videoJsOptions,容易出现配置重复冲突。改为直接通过VideoJS API创建播放器:

import React, { useEffect, useRef } from 'react'
import VideoJs from 'video.js'
import 'video.js/dist/video-js.css';
import "videojs-hotkeys";
import 'videojs-seek-buttons'

const videoJsOptions = {
    controls: true,
    autoplay: false,
    fluid: true,
    loop: false,
    playbackRates: [0.5, 1, 1.5, 2],
    aspectRatio: '12:5',
    html5: {
        vhs: { overrideNative: true },
        nativeAudioTracks: false,
        nativeVideoTracks: false
    },
    plugins: {
        seekButtons: {
            forward: 30,
            back: 10
        },
        hotkeys: {}
    }
}

const VideoPlayer = ({ url, fileType }) => {
    const videoContainerRef = useRef(null)
    const playerRef = useRef(null)

    useEffect(() => {
        // 销毁已有播放器
        if (playerRef.current) {
            playerRef.current.dispose()
        }

        // 创建video元素并挂载
        const videoElement = document.createElement('video')
        videoElement.classList.add('video-js')
        videoContainerRef.current.appendChild(videoElement)

        // 初始化播放器
        const player = VideoJs(videoElement, videoJsOptions, () => {
            player.src({ src: url, type: fileType })
        })

        playerRef.current = player

        return () => {
            if (playerRef.current) {
                playerRef.current.dispose()
                playerRef.current = null
            }
        }
    }, [url, fileType])

    return <div ref={videoContainerRef} />
}

export default VideoPlayer

验证步骤

  1. 打开Chrome开发者工具的Network面板
  2. 播放视频并尝试seek,观察请求状态码是否为206 Partial Content
  3. 检查响应头是否包含Accept-Ranges: bytes和Content-Range字段

内容的提问来源于stack exchange,提问作者theWorstCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 13:10:50