一.ContentType
ContentType指请求体的编码类型,是请求头中的1个键值对,常见的有3种(即下面的3个小标题)
1.application/x-www-form-urlencoded:
最常见的POST数据的提交方式
浏览器的原生form表单,如果不设置enctype属性,就会以该方式提交数据
请求类似于下面(用#包起来的部分)的样子(无关的部分已省略):
###########################################################################
#POST http://www.example.com HTTP/1.1 #
#Content-Type: application/x-www-form-urlencoded;charset=utf-8#这是请求头 #
# #
#user=yuan&age=22#这是请求体 #
###########################################################################
2.multipart/form-data:
另1种常见的POST数据的提交方式
使用form表单上传文件时,必须让form表单的enctype=multipart/form-data
示例(用#包起来的部分)如下:
######################################################################################
#POST http://www.example.com HTTP/1.1 #
#Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA #
# #
#------WebKitFormBoundaryrGKCBY7qhFd3TrwA #
#Content-Disposition: form-data; name="user" #
# #
#yuan #
#------WebKitFormBoundaryrGKCBY7qhFd3TrwA #
#Content-Disposition: form-data; name="file"; filename="chrome.png" #
#Content-Type: image/png #
# #
#PNG ... content of chrome.png ... #
#------WebKitFormBoundaryrGKCBY7qhFd3TrwA-- #
######################################################################################
首先生成1个boundary用于分割不同的字段,为了避免与正文内容重复,boundary很长很复杂
然后Content-Type指明了数据以multipart/form-data来编码以及本次请求的boundary的内容
消息主体里按字段个数又分为多个结构类似的部分,每部分都以--boundary开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本/二进制)
如果传输的是文件,还要包含文件名和文件类型信息;消息主体最后以--boundary--标示结束
关于 multipart/form-data 的详细定义,可前往rfc1867查看
这种方式一般用来上传文件,各大服务端语言对其也有着良好的支持
以上2种提交POST数据的方式,都是浏览器原生支持的
并且现行标准中form表单也只原生支持这2种方式和另1种方式
通过<form>的enctype属性指定,默认为application/x-www-form-urlencoded)
enctype还支持text/plain,不过用得非常少
随着更多Web站点,尤其是WebApp使用Ajax进行数据交互,之后可以定义新的数据提交方式,给开发带来更多便利
3.application/json:
现在越来越多地把这个作为请求头,用来告诉Server消息主体是序列化后的JSON str
由于JSON规范的流行,除低版本IE外的各大浏览器都原生支持JSON.stringify
服务端语言也都有处理JSON的函数,故使用JSON不会遇上什么麻烦
并且JSON格式支持比键值对复杂得多的结构化数据,这1点也很有用
二.基于Form表单的文件上传
#语法:
<req>.body:原始的请求体数据
<req>.GET:GET方式提交的数据
<req>.POST:POST方式提交的数据
<req>.FILES:上传的文件数据
#参数说明:
req:HttpRequest对象
<!--upload.html:-->
<body>
<form action="/upload.html" method="POST">
<input type="text" name="user">
<div style="position:relative"> <!--样式见下图2-->
<a>NB上传</a>
<input type="file" name="fi" stype="opacity:0;position:absolute;top:0;left:0">
<!--设置相对于<div>的位置和透明度-->
<!--这个按钮无法通过直接添加CSS代码来修改样式,只能将其透明化,并放在另1个自定制的标签下-->
</div>
<!--<input type="file" name="fi">--> <!--样式见下图1-->
<input type="submit" value="提交">
</form>
</body>
#views.py:
from django.shortcuts import render
from django import forms
class UploadForm(forms.Form):
user=forms.fields.CharField()
fi=forms.fields.FileField()
def upload(req):
if req.method=="GET":
return render(req,"upload.html")
elif req.method=="POST":
'''也可以通过Form类来获取数据
obj=UploadForm(req.POST,req.FILES)
if obj.is_valid:
user=obj.cleaned_data["user"]
fi=obj.cleaned_data["fi"]
'''
user=req.POST.get("user")
fi=req.FILES.get("fi")#fi是1个对象,包括文件大小/文件名称/文件内容等
#fi.name是文件名称,fi.size是文件大小,fi.chunks()是文件内容
f=open(fi.name,"wb")#以wb形式打开1个同名文件
for c in fi.chunks():#fi.chunks()是1个迭代器,只能通过迭代一段段写入
f.write(c)
f.close()
return render(req,"upload.html")
二.基于Ajax的文件上传
1.通过基于JQuery的Ajax和原生Ajax:
这2种方式都可以利用formData对象来封装用户提交的数据,从而实现文件上传
<!--index.html:-->
<body>
<form>
用户名<input type="text" id="user">
头像<input type="file" id="avatar">
<div>
<input type="button" id="ajax-submit" value="ajax-submit">
<input type="button" id="ajax-submit2" value="ajax-submit2">
</div>
</form>
<script src="/static/js/jquery-3.1.1.js">
<script>
$("#ajax-submit").click(function(){
var data=new FormData();
data.append("user",$("#user").val());//添加1个键值对
data.append("avatar_img",$("#avatar")[0].files[0]);//添加文件内容
//.files[0]表示获取其中第1个文件的文件内容
$.ajax({
url:"/index.html",
type:"post",
data:data,
processData:false,//要求Django不处理数据
contentType:false,//要求Django不设置内容类型
success:function(arg){
console.log(arg)
}
})
})
$("#ajax-submit2").click(function(){
var data=new FormData();
data.append("user",$("#user").val());//添加1个键值对
data.append("avatar_img",$("#avatar")[0].files[0]);
var xhr=new XMLHttpRequest;
xhr.onreadystatechange=function () {
if(xhr.readyState==4) {
console.log(xhr.responseText);
}
}
xhr.open("POST","/index.html");
xhr.send(mdata);
})
</script>
</body>
#views.py:
def index(request):
if request.is_ajax():#如果是Ajax请求
print(request.body)#原始的请求体数据
print(request.GET)#GET方式提交的数据
print(request.POST)#POST方式提交的数据
print(request.FILES)#上传的文件数据
return HttpResponse("ok")
return render(request,"index.html")
通过浏览器查看请求头:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryaWl9k5ZMiTAzx3FT
2.通过"伪"Ajax(兼容性最好):
<!--index.html:-->
<iframe id="iframe" name="ifra" style="display:none"></iframe>
<form id="fm" action="/index.html" method=POST target="ifra" enctype="multipart/form-data">
<input type="text" name="k1">
<input type="file" name="k2">
<a onclick="AjaxSub()">提交</a>
</form>
<script src="/static/js/jquery-3.1.1.js"></script>
<script>
function AjaxSub() {
document.getElementById("iframe").onload=reloadIframe;
document.getElementById("fm").submit()
}
function reloadIframe() {//回调函数
var content=this.contentWindow.document.body.innerHTML;
var obj=JSON.parse(content);
console.log(obj);
}
</script>
四.图片预览的实现
头像和预览要上传的图片都是用类似的方法实现的
<!--index.html:-->
<iframe id="iframe" name="ifra" style="display:none"></iframe>
<form id="fm" action="/index.html" method=POST target="ifra" enctype="multipart/form-data">
<input type="file" name="k" onchange="Upload();">
<!--用户上传文件后直接提交-->
</form>
<div>
<h3>预览</he>
<div id="prev"></div>
</div>
<script src="/static/js/jquery-3.1.1.js"></script>
<script>
function Upload() {
document.getElementById("iframe").onload=reloadIframe;
document.getElementById("fm").submit()
}
function reloadIframe() {//回调函数
var content=this.contentWindow.document.body.innerHTML;
var obj=JSON.parse(content);
console.log(obj);
var tag=document.createElement("img");//创建1个<img>
tag.src=obj.data;//设置图片(在Server中的)路径
$("#prev").empty().append(tag);//清空之前上传的图片,再将tag放到#prev中
}
</script>
#views.py:
from django.shortcuts import HttpResponse
import json,os,uuid
def index(req):
ret={"status":True,"data":None,"msg":None}
uid=str(uuid.uuid4())
path=os.path.join("static",nid+obj.name)
obj=req.FILES.get("k")
f=open(path,"wb")
for line in obj.chunks():
f.write(line)
f.close()
ret["data"]=path
return HttpResponse(json.dumps(ret))