通过Django的form及Ajax等多种方式上传文件
如下效果:
方式一、通过Django的Form方式上传文件
思路:前台html创建Form,根据格式要求完善form内标签属性;提交后台后,后台获取前台传来的数据进行处理:文件路径保存至数据库,文件内容保存至本地文件夹中;后台处理完成后刷新前台页面。
1、创建空白Django项目,过程忽略。
2、静态html页面设置:
<form action="/upload.html" method="post" enctype="multipart/form-data">
<input type="text" name="fileName">
<input type="file" name="fileContent">
<input type="submit" value="提交">
</form>
写一个form表单,三个关键标签:text、file、submit,分别存新的文件名、文件内容、提交按钮,由于是文件上传因此form中enctype需要设置成multipart/form-data。
3、views后台设置:
from django.shortcuts import render,redirect
import os
def Upload(request):
if request.method=="GET":
return render(request,"upload.html")
elif request.method=="POST":
# 获取普通input标签值,即文件名
filname=request.POST.get('fileName')
# 获取file类型的input标签值,即文件内容
file=request.FILES.get('fileContent')
# 获取文件后缀名
postfix=file.name.split('.')[1]
# 设置本地文件路径
file_path=os.path.join('static',filname+'.'+postfix)
# 将上传的文件写入本地目录
f=open(file_path,"wb")
for chunk in file.chunks():
f.write(chunk)
f.close()
return redirect("upload.html")
url文件配置:
from Upload import views
urlpatterns = [
path('upload.html',views.Upload),
]
原理:
(1)、当Get请求时,即浏览器打开该网页时,显示上传页面;
(2)、当POST请求时,即点击“提交”按钮时:
①、获取界面传过来的新的文件名及文件内容。
②、分块读取文件内容,并写入到本地目录。
4、页面上动态显示刚刚上传的图片,需做如下改造:
(1)、新建数据库表,用来存储图片路径:
# 图片类
class image(models.Model):
# 路径
file_Path=models.CharField(max_length=32)
(2)、在上传成功时将文件路径保存至数据库,即在views的视图方法的POST中的保存文件后面添加代码:
# 写入成功后将路径保存至数据库
models.image.objects.create(file_Path=file_path)
(3)、页面刷新时展现图片,即在视图方法的GET中读取数据库中添加的图片路径,并将其返给html页面:
if request.method=="GET":
# 获取所有图片
imgs=models.image.objects.all()
return render(request,"upload.html",{"imgs":imgs})
(4)、html页面中增加
<div class="imgs">
{% for obj in imgs %}
<img src="{{ obj.file_Path }}">
{% endfor %}
</div>
(5)、由于Django对静态文件浏览的限制,需要在配置中添加:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static"),
]
方式二、通过原生js实现
思路:html页面,通过FormData承载图片内容,并通过原生的XMLHttpRequest将页面全部信息传输到后台views中;后台views接收后处理并将结果通过json形式返回给前台html页面;前台html页面接收到字符串内容后转换为json对象;创建图片标签,并将接收到的图片地址赋值。
1、url配置如下:
from Upload import views
urlpatterns = [
path('uploadjs.html', views.Upload_js),
]
2、views视图函数:
def Upload_js(request):
if request.method == "GET":
# 获取所有图片
imgs = models.image.objects.all()
return render(request, "upload_js.html", {"imgs": imgs})
elif request.method == "POST":
# 获取普通input标签值,即文件名
filname = request.POST.get('fileName')
# 获取file类型的input标签值,即文件内容
file = request.FILES.get('fileContent')
# 获取文件后缀名
postfix = file.name.split('.')[1]
# 设置本地文件路径
file_path = os.path.join('static', filname + '.' + postfix)
# 将上传的文件写入本地目录
f = open(file_path, "wb")
for chunk in file.chunks():
f.write(chunk)
f.close()
# 写入成功后将路径保存至数据库
models.image.objects.create(file_Path=file_path)
#将状态及文件路径通过json形式返回至html页面
ret={'status':True,'path':file_path}
return HttpResponse(json.dumps(ret))
3、前台html页面
<body>
<input type="text" name="fileName" id="fileName">
<input type="file" name="fileContent" id="fileContent">
<input type="button" value="提交" onclick="upload();">
<div class="imgs">
{% for obj in imgs %}
<img src="{{ obj.file_Path }}">
{% endfor %}
</div>
<script>
function upload() {
//创建FormData用于存储文件内容
var dict=new FormData();
dict.append("fileName",document.getElementById('fileName').value);
dict.append("fileContent",document.getElementById('fileContent').files[0]);
var xml=new XMLHttpRequest();
//uploadjs.html即url.py中配置的后台views
xml.open("post",'/uploadjs.html',true);
xml.onreadystatechange=function(){
if (xml.readyState==4 && xml.status==200) {
//传输成功时将视图返回回来的内容通过json接收
var obj=JSON.parse(xml.responseText);
if(obj.status){
//创建图片标签,并将后台传回来的图片地址加上,显示在页面中
var img=document.createElement('img');
img.src='/'+obj.path;
document.getElementsByClassName('imgs')[0].appendChild(img);
}
}
};
xml.send(dict)
}
</script>
</body>
方式三、通过JQuery实现
原理:将方式二中原生js部分改为JQuery形式,其他无变化。
html页面代码如下:
<body>
<input type="text" name="fileName" id="fileName">
<input type="file" name="fileContent" id="fileContent">
<input type="button" value="提交" onclick="upload();">
<div class="imgs">
{% for obj in imgs %}
<img src="{{ obj.file_Path }}">
{% endfor %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
function upload() {
//创建FormData用于存储文件内容
var dict=new FormData();
dict.append("fileName",document.getElementById('fileName').value);
dict.append("fileContent",document.getElementById('fileContent').files[0]);
$.ajax({
url:'/uploadjq.html',
type:'post',
data:dict,
processData: false,// 告诉jQuery不要去处理发送的数据(必须设置)
contentType: false, // 告诉jQuery不要去设置Content-Type请求头(必须设置)
dataType:'JSON',
success:function (arg) {
if(arg.status){
//创建图片标签,并将后台传回来的图片地址加上,显示在页面中
var img=document.createElement('img');
img.src='/'+arg.path;
$('.imgs')[0].appendChild(img);
}
}
})
}
</script>
</body>
注意:由于使用的是FormData对象,需要将processData、contentType设置成false,至于为什么要这么设置,详见:
[#1] 浅谈contentType = false
[#2] 原生ajax请求 怎么设置processData这个参数?
方式四、通过iframe及form表单,views后台代码与方式二一致
原理:将views后台返回的数据传给iframe页面标签,触发iframe的onload事件时实现img图片动态加载。
1、原生js时,获取iframe对象内容通过document.getElementById('iframe的ID').contentWindow.document.getElementById('元素的ID');contentWindow代表frame和iframe内部的窗口对象。html前台页面如下:
<body>
<form action="/uploadiframe.html" method="post" enctype="multipart/form-data" target="img_igrame">
<iframe id="img_igrame" name="img_igrame" onload="loadiframe(this)" ></iframe>
<input type="text" name="fileName">
<input type="file" name="fileContent">
<input type="submit" value="提交">
</form>
<div class="imgs">
{% for obj in imgs %}
<img src="{{ obj.file_Path }}">
{% endfor %}
</div>
<script>
function loadiframe(self) {
// 获取iframe内部的内容
var str_json=self.contentWindow.document.getElementsByTagName('body')[0].innerText;
var obj = JSON.parse(str_json);
if (obj.status){
var img = document.createElement('img');
img.src = "/" + obj.path;
document.getElementsByClassName('imgs')[0].appendChild(img);
}
}
</script>
</body>
2、jQuery时,通过.contents() 方法;.contents() 方法允许我们检索 DOM 树中的这些元素的直接子节点,并用匹配元素构造新的 jQuery 对象。.contents() 和 .children() 方法类似,不同的是前者在结果 jQuery 对象中包含了文本节点以及 HTML 元素。.contents() 方法也可以用于获得 iframe 的内容文档,前提是该 iframe 与主页面在同一个域。代码如下:
<body>
<form action="/uploadiframe.html" method="post" enctype="multipart/form-data" target="img_igrame">
<iframe id="img_igrame" name="img_igrame" onload="loadiframe(this)" ></iframe>
<input type="text" name="fileName">
<input type="file" name="fileContent">
<input type="submit" value="提交">
</form>
<div class="imgs">
{% for obj in imgs %}
<img src="{{ obj.file_Path }}">
{% endfor %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
function loadiframe(self) {
// 获取iframe内部的内容
var str_json = $('#img_igrame').contents().find('body').text();
var obj = JSON.parse(str_json);
if (obj.status){
var img = document.createElement('img');
img.src = "/" + obj.path;
$('#imgs').append(img);
}
}
</script>
</body>
=======
总结:
通过Django的Form方式上传文件,简单,但无法局部刷新实现ajax效果;
通过js或jQuery方式,需要使用FormData对象,仍有些浏览器不兼容;
通过Form+iframe方式上传文件,兼容性最好。
完整源代码请见:https://download.****.net/download/baobao267/10718732