如何在Django中通过AJAX POST请求上传视频文件?
摄像头录屏上传后端的实现优化与安全指南
CSRF令牌的正确配置方式
Django中处理AJAX POST请求的CSRF防护,你当前的写法已具备基础逻辑,可通过以下方式优化得更可靠:
模板中稳定获取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中的令牌。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
相关产品推荐
相关产品推荐

