AJAX上传图片至Django后端丢失,request.FILES为空求助
AJAX向Django传文件时request.FILES为空的解决方案
问题现象
通过new FormData()收集表单数据并附加额外字段,AJAX提交时前端控制台能看到图片文件信息,但Django后端:
json.loads(request.body)中图片对应的键为空字典request.FILES为空
模板表单代码
<form method="POST" action="" onsubmit="set_table(event)" id="myform" enctype="multipart/form-data"> {% csrf_token %} <div class="sections1" name="titre"> <label for="title">项目名称</label> <input type="text" name="title" maxlength="30" required class="postgrid" style="width:250px;"> </div> <div class="sections1"> <label for="cathegorie">选择项目对应的类别</label> <select class="postgrid" name="cathegorie" > {% for cats in cat %} <option value="{{ cats}}">{{ cats }}</option> {% endfor %} </select> </div> <div class="sections" name="pb"> <label for="probleme">项目针对的问题</label> <textarea rows="10" cols="30" name="probleme" required class="postgrid" data-name="probleme_name"></textarea> <div name="shower" id="show" class="rowbench"></div> </div> <div class="sections" name="ds"> <label for="description">项目描述</label> <textarea rows="30" cols="40" name="description" data-name="description_name" required class="postgrid"></textarea> <div name="shower" id="show" class="rowbench"></div> </div> <div class="sections" name="sv"> <label for="services">项目创建的服务</label> <textarea rows="10" cols="40" name="services" required data-name="services_name" class="postgrid"></textarea> <div name="shower" id="show" class="rowbench"></div> </div> <div class="sections1" name="titre"> <table style="border: 1px solid black; border-collapse:collapse; margin:auto; width:80%;" id="table"> <tbody id="tb"> <tr name="head"> <th>人员</th> <th>数量</th> <th>用途</th> </tr> <tr> <td><input type="text" style="margin:auto; width:95%;" placeholder="例:数据分析师" id="personnel"></td> <td><input type="text" style="margin:auto;width:95%;" placeholder="例:5" id="number"></td> <td><input type="text" style="margin:auto;width:95%;" placeholder="例:数据收集与处理" id="for"></td> </tr> </tbody> </table> <button style="color:rgb(32, 148, 171); background-color:black; position:absolute; right:10px; bottom:10px;" onclick="add_row(event)">+</button> </div> <button Class="post_creator" style="position:relative; bottom:4px; margin:85px; " type="submit">提交</button> </form> <script src="{% static 'projectcreation.js' %}"></script> <script> arr=document.querySelectorAll('.sections'); for (let i=0; i<arr.length; i++){ let a=arr[i]; a.innerHTML+=` <div style="position: relative;"> <div style="width:40px; height: 35px; "> <input type="file" accept="image/*" class="invisible" multiple oninput="showinput(event)" name="${a.getAttribute('name')}_image" data-name="${a.getAttribute('name')}"> </div> <img src="{% static 'im.png' %}" class="pinner"> </div>`; //console.log(a.getAttribute('name')); } </script>
原JS代码
function accept(formdata){ const data1=Object.fromEntries(formdata.entries()); console.log(data1);//打印所有内容,能看到图片名称 $.ajax( {type:'POST', url:document.getElementById('url').getAttribute('data-url'), data:JSON.stringify({"formdata":data1}), headers:{ "X-Requested-With":"XMLHttpRequest", "X-CSRFToken":getCookie("csrftoken"), }, success:(data) =>{ console.log('data'); }, dataType:'json', contentType:false, processData:false } ); } function set_table(event){ event.preventDefault(); const table = document.getElementById('tb'); const list=table.children; console.log(list.length); let arr=[]; for (let row =0; row<list.length;row++ ){ let rows=list[row]; if (rows.getAttribute('name') !='head'){ let data={ "agent":rows.querySelector('#personnel').value, "number":rows.querySelector('#number').value , "work":rows.querySelector('#for').value }; arr.push(JSON.stringify(data)); } } //console.log(event.target.data); const formdata=new FormData(event.target); formdata.append('tab',arr); accept(formdata); }
原Django视图代码
def project_creation_page(request): context={"cat":project_cat} is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest' if is_ajax: print(34) if request.method=='POST': data=json.loads(request.body) print(data) for file ,files in request.FILES: print(file, files.name)#无输出 return render(request,"Projectscreation.html",context)
问题根源
- File对象无法JSON序列化:把FormData转成Object后用
JSON.stringify()发送,File对象会被序列化成空字典,后端自然接收不到文件内容。 - 错误的请求格式:发送JSON格式的请求,Django不会把文件解析到
request.FILES里,只有multipart/form-data格式的请求才会触发文件解析。
解决方案
1. 修改JS代码,直接发送FormData
去掉FormData转Object和JSON序列化的步骤,直接将FormData作为AJAX的data参数发送:
function accept(formdata){ // 直接发送FormData,不要转成JSON $.ajax({ type:'POST', url:document.getElementById('url').getAttribute('data-url'), data: formdata, // 直接传FormData对象 headers:{ "X-Requested-With":"XMLHttpRequest", "X-CSRFToken":getCookie("csrftoken"), }, success:(data) =>{ console.log('提交成功:', data); }, contentType:false, // 必须设为false,让浏览器自动设置multipart/form-data边界 processData:false // 必须设为false,避免jQuery处理FormData }); } function set_table(event){ event.preventDefault(); const table = document.getElementById('tb'); const list=table.children; let arr=[]; for (let row =0; row<list.length;row++ ){ let rows=list[row]; if (rows.getAttribute('name') !='head'){ let data={ "agent":rows.querySelector('#personnel').value, "number":rows.querySelector('#number').value , "work":rows.querySelector('#for').value }; arr.push(data); // 这里不用提前JSON.stringify,后面统一处理 } } const formdata=new FormData(event.target); // 把数组转成JSON字符串再append,避免FormData传数组时后端收到多个tab参数 formdata.append('tab', JSON.stringify(arr)); accept(formdata); }
2. 修改Django视图代码,正确获取数据
不再解析request.body为JSON,而是从request.POST获取普通表单字段,request.FILES获取文件:
import json def project_creation_page(request): context={"cat":project_cat} is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest' if is_ajax and request.method=='POST': # 获取普通表单数据 title = request.POST.get('title') cathegorie = request.POST.get('cathegorie') probleme = request.POST.get('probleme') # 获取表格数据:解析JSON字符串 tab_data = json.loads(request.POST.get('tab', '[]')) # 获取文件 pb_image = request.FILES.get('pb_image') ds_image = request.FILES.get('ds_image') sv_image = request.FILES.get('sv_image') # 打印测试 print("表单数据:", title, cathegorie) print("表格数据:", tab_data) if pb_image: print("pb_image文件名:", pb_image.name) if ds_image: print("ds_image文件名:", ds_image.name) if sv_image: print("sv_image文件名:", sv_image.name) # 后续可以处理保存文件等逻辑 # 比如保存pb_image到媒体目录: # with open(f'media/{pb_image.name}', 'wb+') as destination: # for chunk in pb_image.chunks(): # destination.write(chunk) return render(request,"Projectscreation.html",context)
内容的提问来源于stack exchange,提问作者Lord vic
相关产品推荐
相关产品推荐

