Django框架 文件上传

一.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))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值