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

如何在Django中通过AJAX POST请求上传视频文件?

摄像头录屏上传后端的实现优化与安全指南

CSRF令牌的正确配置方式

Django中处理AJAX POST请求的CSRF防护,你当前的写法已具备基础逻辑,可通过以下方式优化得更可靠:

  1. 模板中稳定获取CSRF令牌:
    推荐从cookie读取令牌,避免模板渲染异常导致的令牌缺失:

    <script>
    function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    const csrftoken = getCookie('csrftoken');
    </script>
    

    注意:确保Django配置中CSRF_COOKIE_HTTPONLY为False(默认值),否则前端无法读取cookie中的令牌。

  2. AJAX请求中携带令牌:
    你当前在headers中添加X-CSRFToken的方式是正确的,保持该配置即可:

    headers: {
        'X-CSRFToken': csrftoken,
    },
    

前端代码优化

现有代码存在重复录制片段叠加、无错误处理等问题,优化后的完整代码如下:

window.onload = function () {
    let parts = [];
    let mediaRecorder;
    let stream;

    // 请求媒体权限并处理异常
    navigator.mediaDevices.getUserMedia({ audio: true, video: true})
    .then(mediaStream => {
        stream = mediaStream;
        document.getElementById("video").srcObject = stream;
        
        // 开始录制按钮逻辑
        document.getElementById("btn").onclick = function () {
            // 防止重复点击开始录制
            if (mediaRecorder && mediaRecorder.state !== 'inactive') return;
            
            parts = []; // 每次开始录制清空历史片段
            mediaRecorder = new MediaRecorder(stream);

            mediaRecorder.start(1000);
            console.log("录制已启动");

            mediaRecorder.ondataavailable = function(e) {
                parts.push(e.data);
            };
        };
    })
    .catch(err => {
        console.error("媒体权限请求失败:", err);
        alert("无法访问摄像头/麦克风,请检查浏览器权限设置");
    });

    // 停止录制并上传逻辑
    document.getElementById("stopbtn").onclick = function () {
        if (mediaRecorder && mediaRecorder.state === 'recording') {
            mediaRecorder.stop();
            console.log("录制已停止");
            
            // 生成视频文件并上传
            const blob = new Blob(parts, { type: "video/webm" });
            const formData = new FormData();
            // 用时间戳生成唯一文件名,避免重复覆盖
            const filename = `recording_${Date.now()}.webm`;
            formData.append("video_blob", blob, filename);

            $.ajax({
                url: '/recorder/',
                type: 'POST',   
                success: function (res) {
                    console.log("视频上传成功:", res.message);
                    parts = []; // 上传成功后清空片段
                },      
                error: function(xhr, status, err) {
                    console.error("上传失败:", err);
                    alert("视频上传失败,请重试");
                },
                data: formData,
                headers: {
                    'X-CSRFToken': csrftoken,
                },                
                cache: false,
                contentType: false,
                processData: false
            });
        }
    };
};

后端代码完善

后端视图需补充权限校验、文件验证和异常处理,确保稳定安全:

from django.http import JsonResponse
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import VideoRecord  # 替换为你的实际模型类

@login_required  # 仅允许登录用户上传
def save_video(request):
    if request.method == "POST":
        video_file = request.FILES.get("video_blob")
        if not video_file:
            return JsonResponse({"message": "未检测到视频文件"}, status=400)
        
        # 限制文件类型
        allowed_types = ['video/webm']
        if video_file.content_type not in allowed_types:
            return JsonResponse({"message": "仅支持WebM格式视频"}, status=400)
        
        # 限制文件大小(示例:最大50MB)
        max_size = 50 * 1024 * 1024
        if video_file.size > max_size:
            return JsonResponse({"message": "视频大小不能超过50MB"}, status=400)
        
        try:
            # 创建记录,关联当前登录用户
            VideoRecord.objects.create(
                user=request.user,
                video_file=video_file
            )
            return JsonResponse({"message": "视频上传成功"})
        except Exception as e:
            return JsonResponse({"message": f"保存失败: {str(e)}"}, status=500)
    
    return render(request, 'recorder/index.html')

配套模型示例(需存放在models.py中):

from django.db import models
from django.contrib.auth.models import User

class VideoRecord(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    video_file = models.FileField(upload_to='recordings/')  # 文件存储到MEDIA_ROOT/recordings/目录
    created_at = models.DateTimeField(auto_now_add=True)

注意:需在Django配置中设置MEDIA_ROOT和MEDIA_URL,确保文件能正确存储和访问。

关键安全注意事项

  • CSRF防护:禁止使用csrf_exempt装饰器,确保所有POST请求携带有效CSRF令牌,防止跨站请求伪造攻击。
  • 文件上传安全:严格限制文件类型、大小,后端重新生成安全文件名,避免存储恶意文件或超大文件占用资源。
  • 权限控制:添加登录校验,仅允许授权用户上传内容,防止未授权访问。
  • 数据验证:后端二次验证文件格式(如检查文件头),避免伪装成视频的恶意文件。
  • 资源清理:前端上传成功后清空录制片段,避免内存占用过高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:25:57