Ajax上传数据与文件与后端Django数据接收——两端引渡数据协议
日前前言
这两天做管理台,重写了前端后端所有关于管理的页面、功能。每一个管理卡片都通过jq的load()方法异步加载。这样的方式极大的节省了管理台的资源加载过程和时间。
但同时也引出了一些问题,当我想提交POST表单的时候,不能直接加入在html结构中直接加入{% csrf_token %}进行后端验证,这个时候,ajax便又登场了,但由于前端发送的数据和后端接收的数据始终不同—_—!!就跟标题一样,漫长的引渡过程,耗了我整整一天的时间才解决,接下来将详细描述踩坑过程
在这里插入图片描述
一、前端——把表单的架子画出来
在这里插入图片描述<link rel="stylesheet" href="articlesImportMD.min.css">
<form class="g-body-importMD" action='#' enctype="multipart/form-data" method="post">
<h2>导入MD</h2>
<div><h3>分类选择:</h3></div>
<div><input type="checkbox" name='category' value='2' id='s-category'>Python</div>
<div><input type="checkbox" name='category' value='3' id='s-category'>Django</div>
<div><input type="checkbox" name='category' value='4' id='s-category'>JavaScript</div>
<div><input type="checkbox" name='category' value='5' id='s-category'>Css</div>
<div><input type="checkbox" name='category' value='6' id='s-category'>Linux</div>
<div><input type="checkbox" name='category' value='7' id='s-category'>安全</div>
<div><input type="checkbox" name='category' value='8' id='s-category'>树莓派</div>
<h3><span>导入MD文件:</span><input type="file" name='htmlF' id='s-htmlF' accept=".html"></h3>
<h3><span>导入附属图片:</span><input type="file" name='imgF' id='s-imgF' multiple="multiple" accept="image/*"></h3>
<button type='submit'>发布文章</button>
<br>
<div class="g-body-importMD-status" style="display:none"></div> <!--后面显示状态信息使用--!>
</form>
加以了一些样式过后差不多就是这个样式,这步不用怎么关心,只要有功能就好了
在这里插入图片描述
二、前端——如何通过js获取表单的数据和文件对象
通过ajax发送数据和文件两种东西的话,是不能直接通过$('elm').val()
这种方式获取到值的。需要新构造一个FormData
然后去继承html中的form
var formDataOld = new FormData($('.g-body-importMD')); // 继承
// 用get方法获取到htmlF这个FileInput中的值,注意,直接打印FormData是没有数据的
var fileHtml = formDataOld.get('htmlF');
这里有个特殊情况,MD文件我们只需要获取它的文件内容,如果把他也发送给服务器必定会造成响应速度减慢和资源浪费,怎么办呢?这里再新构建一个FormData
,然后一个一个的append进FormData
即可
// 自定义的表单
var formData = new FormData();
// 获取用户上传文件的信息(html表单)
var formDataOld = new FormData($('.g-body-importMD'));
var fileHtml = formDataOld.get('htmlF');
// 获取文章分类
$("input:checkbox[name=category]:checked").each(function (i) {
formData.append('categoryList',$(this).val()); // 若需要对应一个列表,那就for循环append即可
});
// 获取文章标题(获取的是文件名,所以清一下后缀)
var fileTitle = fileHtml.name.replace('.html', '');
// 加入所有附属图片到自构建表单内
var photoFiles = document.getElementById("s-imgF").files;
for(var i=0;i<photoFiles.length;i++){
formData.append('imgF',photoFiles[i]);
}
// 获取文章内容
var reader = new FileReader();
reader.readAsText(fileHtml, 'UTF-8');
reader.onload = function () {
var fileContext = this.result;
// 新数据加入表单
formData.append('fileTitleH', fileTitle);
formData.append('fileContextH', fileContext);
};
这一系列的操作过后,我们最终得到的是一个名为formData
的表单对象,可通过上一步的formData.get()
函数分别获取到categoryList
、fileTitleH
、fileContextH
、imgF
这四个键的值,如下图:
这里看着可能会感觉很乱,怎么就突然冒出来一大堆代码了,心情就跟表情包一样,但是实际上都是些重复的操作,复制到自己的js里执行一下就会恍然大悟的
三、前端——如何携带建好的表单对象发送给后端Django
只要我们把上一步构建好的formData
对象直接加入ajax的data里就可以了,Django在获取的时候会直接解析这个表单
其中有一个小问题,就是Djang的csrf会返回403,详细的解决方法可以参看之前写过的文章Ajax常用方法与后端(Django)通信403的解决方案。
<script src="jquery.cookie.js"</script>
$.ajax({
url: '/addArticleMessy/',
type: 'POST',
processData: false, // 不处理数据(必须)
contentType: false, // 不设置内容类型(必须)
headers: {
"X-CSRFToken": $.cookie('csrftoken') // Django 403处理
},
data: formData, // 直接填入自定义表单对象
success: function (data) { // 请求完成后的处理操作
$('.g-body-importMD-status').empty();
$('.g-body-importMD-status').html('<div style="color:#19b955">【' + fileTitle + '】发布成功!</div>');
},
error: function () {
$('.g-body-importMD-status').empty();
$('.g-body-importMD-status').html('<div style="color:#c9302c">服务器数据处理失败,详情查看服务器日志</div>');
}
});
四、后端——如何接收到相应数据
这里就不讲url、model文件的设置了,后端相对前端的操作要简便一些。
由于没有什么重要的点,就把整段代码展示出来,这里说一下大概的步骤吧:
- 使用
POST.get()
和FILEFS.getlist()
获取数据和文件对象 - 判断是否有存储该篇文章的文件夹(防止报系统错误)
- 存储图片
def addHF(self):
today_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
# 获取ajax发来的表单信息
# 一个键的值如果是一个列表的话,请使用getlist获取,否则返回字节对象,我就在这儿卡了好久...
category_list = self.POST.getlist('categoryList') # 分类1/2
fileTitleH = self.POST.get('fileTitleH') # 文章标题
fileContextH = self.POST.get('fileContextH') # 文章内容
files = self.FILES.getlist('imgF') # 附属图片
# 信息初始化
name = fileTitleH # 文章名为文件名
category2 = '' # 多标签获取与判断
if len(category_list) == 1:
category1 = category_list[0]
else:
category1 = category_list[0]
category2 = category_list[1]
def savePhotos(): # 附属图片的存储,写成函数方便下面判断文件夹进行调用
for f in files: # 通过图片名进行去重操作
file_exist = os.path.exists('media/articlePhotos/' + fileTitleH + '/' + f.name)
if file_exist:
pass
else:
# 这里一定要使用wb方式写入,chunks获取的数据是字节类型
with open('media/articlePhotos/' + fileTitleH + '/' + f.name,'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
if not files: # 文件夹存在判断,防止系统错误
pass
else:
dir_exist = os.path.exists(r'media/articlePhotos/' + fileTitleH)
if dir_exist:
savePhotos()
else:
os.mkdir(r'media/articlePhotos/' + fileTitleH)
savePhotos()
# 创建一部分字段并存储
addHF = Article(name=name, time=today_time, category1_id=category1, category2_id=category2)
addHF.context = context
# 存进数据库
addHF.save()
return render(self, 'jump/manager_jump.html')
最后
到这里,所有的代码整合测试一下,就有一个简单的文章发布功能了。
后面肯定会完善整个后台管理系统的,如果评论喜欢的人多的话,应该也会出一个相关的教程,谢谢大家!
本文作者: Messy
原文链接:https://www.messys.top/detail/67
版权声明: 本博客所有文章除特别声明外, 均采用 CC BY-NC-SA 4.0 许可协议. 转载请注明出处!
网友评论