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

Django结合JS实现摄像头多图上传并赋值给ImageField问题咨询

解决方案

你现有的Django后端逻辑完全不需要修改,只需要调整前端代码,把canvas拍摄的图片转换为File对象填充到formset对应的文件输入框即可,具体修改步骤如下:

1 调整HTML结构

首先修正原HTML中摄像头组件重复渲染的问题,将公共摄像头区域移到循环外部,同时给每个表单添加「拍摄填充」按钮:

<!-- 公共摄像头区域 放在form外面或者顶部都可以 -->
<div id="camera-area" style="display:none; margin: 1rem 0;">
  <video id="player" controls autoplay width="320"></video>
  <button id="capture">确认拍摄</button>
  <button id="close-camera">关闭摄像头</button>
  <canvas id="canvas" width=320 height=240 style="display:none;"></canvas>
</div>

<button id="addButton" class="px-4 py-1 pb-2 text-white focus:outline-none header rounded-xl">
    {% trans "add new form" %}
    <i class="bi bi-plus"></i>
</button>  

<form action="" method="POST" enctype="multipart/form-data" dir="ltr">{% csrf_token %}
    {{images.management_form}}
    <div id="images" class="mt-3">
    {% for form in images.forms %}
        {{form.id}}          
        <div class="form-group border p-2 rounded-lg mb-2">
            <div class="mb-2">
                {{ form.docs | add_class:'form-control-file' }}
                <!-- 新增拍摄按钮 绑定当前表单索引 -->
                <button type="button" class="take-photo-btn" data-index="{{ forloop.counter0 }}">使用摄像头拍摄</button>
            </div>
            {% if form.DELETE %}
                <div>{{ form.DELETE }} 删除当前图片</div>
            {% endif %}
        </div>
    {% endfor %}
    </div>

    <!-- 空表单模板 也要保留拍摄按钮的占位符 -->
    <div id="formset" style="display: none;">
        <div class="form-group border p-2 rounded-lg mb-2">
            {{ images.empty_form.docs | add_class:'form-control-file' }}
            <button type="button" class="take-photo-btn" data-index="__prefix__">使用摄像头拍摄</button>
            {% if images.empty_form.DELETE %}
                <div>{{ images.empty_form.DELETE }} 删除当前图片</div>
            {% endif %}
        </div>
    </div>

    <button class="header pt-2 text-white px-4 p-1 rounded-lg mt-4">{% trans "save" %}</button>
</form>

2 修改前端JS代码

核心逻辑是:点击拍摄按钮时记录当前要填充的表单索引,拍摄完成后将canvas内容转换为File对象,通过DataTransfer接口填充到对应文件输入框:

const player = document.getElementById('player');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const captureButton = document.getElementById('capture');
const cameraArea = document.getElementById('camera-area');
const closeCameraBtn = document.getElementById('close-camera');
// 记录当前要填充的表单索引
let currentActiveIndex = null;
const constraints = {
  video: true,
};

// 拍摄按钮绑定事件
captureButton.addEventListener('click', (e) => {
  e.preventDefault();
  if (currentActiveIndex === null) return;
  // 绘制帧到canvas
  context.drawImage(player, 0, 0, canvas.width, canvas.height);
  // 转换canvas为jpeg格式的File对象
  canvas.toBlob(function(blob) {
    // 生成文件名 按时间戳命名避免重复
    const fileName = `camera_${Date.now()}.jpeg`;
    const file = new File([blob], fileName, { type: 'image/jpeg' });
    // 构造DataTransfer对象 用于填充文件输入框
    const dataTransfer = new DataTransfer();
    dataTransfer.items.add(file);
    // 找到对应索引的文件输入框 赋值
    const fileInput = document.getElementById(`id_form-${currentActiveIndex}-docs`);
    fileInput.files = dataTransfer.files;
    // 拍摄完成关闭摄像头
    closeCamera();
    alert('拍摄完成,已填充到对应表单');
  }, 'image/jpeg', 0.9); // 0.9是图片质量 可调整
});

// 关闭摄像头逻辑
function closeCamera() {
  if (player.srcObject) {
    player.srcObject.getTracks().forEach(track => track.stop());
  }
  cameraArea.style.display = 'none';
  currentActiveIndex = null;
}
closeCameraBtn.addEventListener('click', closeCamera);

// 每个表单的拍摄按钮绑定事件
document.addEventListener('click', function(e) {
  if (e.target.classList.contains('take-photo-btn')) {
    currentActiveIndex = e.target.dataset.index;
    // 显示摄像头区域 启动摄像头
    cameraArea.style.display = 'block';
    navigator.mediaDevices.getUserMedia(constraints)
      .then((stream) => {
        player.srcObject = stream;
      })
      .catch(err => {
        alert('无法访问摄像头,请检查权限设置');
        closeCamera();
      });
  }
});

// 原有的新增表单逻辑 不需要大改 只需要确认索引正确
$('#addButton').click(function() {
    var form_dex1 = $('#id_form-TOTAL_FORMS').val();
    $('#images').append($('#formset').html().replace(/__prefix__/g,form_dex1));
    $('#id_form-TOTAL_FORMS').val(parseInt(form_dex1) + 1);    
});       

3 逻辑验证

修改完成后测试流程:

  • 点击对应表单的「使用摄像头拍摄」按钮,授权后调起摄像头
  • 拍摄完成后自动填充到对应文件输入框
  • 可点击新增表单按钮添加多个图片位,重复拍摄流程实现多图上传
  • 提交表单时后端逻辑和原文件上传完全兼容,不需要做任何修改

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 06:39:04