ajax和原生ajax、文件的上传

ajax理解:

  • ajax发送的请求是异步处理的。也就是说如下形式:
 1 function f1(){
 2 var a=2
 3 $.ajax(
 4 {
 5  .......
 6 success:function(){
 7 a=1
 8 return a 
 9 }
10 }
11 )
12 return a
13 }
f1()

 

如上函数f1套ajax请求,并获取ajax的匿名函数中a的值,在实际执行 f1()函数过程中,到ajax请求的时候,当前执行函数f1函数的主线程继续往下执行,而开辟另一个子线程执行ajax请求,所以最后f1返回的值1 而不是2.

因为ajax请求是异步请求。所以想取ajax中的匿名函数的返回值,可以设置标志位来循环获取。但是不建议这么做。

参考地址:http://www.dewen.net.cn/q/13078

一:form表单上传文件

前端页面通过input标签 和form表单给后台服务器上传文件。

需要注意的是:前端页面action里的url必须要以‘/’结尾。因为如果不以'/'结尾,django在重定向url有可能丢失数据和文件。

前端页面code:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <form action="/upload/" method="post" enctype="multipart/form-data">
 9  <div>
10      <input type="file" name="img" value="上传文件" />
11  </div>
12  <div>
13  <input type="submit" value="提交" />
14  </div>
15 </form>
16 </body>
17 </html>

 后端代码:

1 def upload(request):
2     if request.method=='POST':
3         img=request.FILES.get('img')
4         f=open(os.path.join('static',img.name),'wb')
5         for chunk in img.chunks():
6             f.write(chunk)
7         f.close()
8         return redirect('/static/'+img.name)
9     return render(request,'upload.html')

 注意:

  • 后端接收文件的时候,需要使用:request.FILES.get()类似POST.get。是从后端上传的文件找相应的文件。
  • request.FILES.get(‘filename’)获取的是文件的对象。我们print下该对象的信息。
1     if request.method=='POST':
2         img=request.FILES.get('img')
3         print(type(img))
4 <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>

 

img为django.core.files.uploadedfile.InMemoryUploadedFile对象,我们导入下,查看下该对象有什么方法。

需要注意的是:name 字段,name字段为父类中的构造方法的字段。

该字段为为文件的名字:img.name

还有chunks方法:

有yield 说明方法chunks()可以被迭代。也就说客户单上传的文件一点一点上传到服务器端的内存中。

  • 所以基于上面我们可以对chunks()进行迭代,来把从客户单传来的文件一点一点的写入咱们的定义的文件中。注意上传来文件是bytes。

总结:

  1. form上传文件,会刷新页面。用户交互不好。
  2. 你会想到:用jquey,先不说jquery怎么去实现,首先,如果你手机app方面,你不可能让用户的浪费流量去加载jquery。

基于上面的问题,我们接下来介绍原生ajax。

二:原生ajax:我们知道,jquery的ajax,并不是ajax自己实现,他是通过浏览器的一个组件叫做:XmlHttpResquest模块来实现,是这个模块让ajax具有发送和接收数据的能力。所以我可以根据这个模块来写原生ajax。

XmlHttpRequest模块的方法:

xhr=new XmlHttpRequest()

 1 a. xhr.open(String method,String url,Boolen async)
 2    用于创建请求,(在发送get或者post请求前需要创建请求)
 3     
 4    参数:
 5        method: 请求方式(字符串类型),如:POST、GET、DELETE...
 6        url:    要请求的地址(字符串类型)
 7        async:  是否异步(布尔类型)默认是异步处理。因为如果设置成同步处理的话,当我们发送请求的时候,需要等待"请求",此时网页会"停顿"。等待请求结束,显然我们不能接受这样。
 8  
 9 b. xhr.send(String body)
10     用于发送请求 类似于ajax里的data:{'k1':'v1'}
11  
12     参数:
13         body: 要发送的数据(字符串类型)
14  
15 c. xhr.setRequestHeader(String header,String value)
16     用于设置请求头   在get的请求的时候不需要设置这个参数,如果我们用send()方法发送post请求的时候,需要设置请求头。如果用FormData的时候,不需要设置请求头。请求头的作用,是告诉服务端该如何处理我们发送的数据。编码、格式等。
17  
18     参数:
19         header: 请求头的key(字符串类型)
20         vlaue:  请求头的value(字符串类型)
21  
22 d. String getAllResponseHeaders()
23     获取所有响应头
24  
25     返回值:
26         响应头数据(字符串类型)
27  
28 e. String getResponseHeader(String header)
29     获取响应头中指定header的值
30  
31     参数:
32         header: 响应头的key(字符串类型)
33  
34     返回值:
35         响应头中指定的header对应的值
36  
37 f. xhr.abort()
38  
39     终止请求

如果上是XmlHtppRequest模块的方法。

总结:

  • 在我们发送请求的时候,包含两部分,请求头和请求内容,2者之间用2个换行来隔开。相应的也有响应的头和响应内容也是用2个换行来隔开。

 

XmlHttpRequest的属性(和python的属性是一致的)

 1 a. Number readyState
 2    状态值(整数)
 3  
 4    详细:
 5       0-未初始化,尚未调用open()方法;
 6       1-启动,调用了open()方法,未调用send()方法;
 7       2-发送,已经调用了send()方法,未接收到响应;
 8       3-接收,已经接收到部分响应数据;
 9       4-完成,已经接收到全部响应数据;
10  
11 b. Function onreadystatechange
12    当readyState的值改变时自动触发执行其对应的函数(回调函数)
13  
14 c. String responseText
15    服务器返回的数据(字符串类型)通过这属性获得返回数据。
16  
17 d. XmlDocument responseXML
18    服务器返回的数据(Xml对象)
19  
20 e. Number states
21    状态码(整数),如:200404...
22  
23 f. String statesText
24    状态文本(字符串),如:OK、NotFound...

 举例:

1 <input type="button" οnclick="Xhr(this)" value="xhr">

js:

1     function Xhr(ths){
2         var xhr= new XMLHttpRequest();
3         xhr.open('get','/xmlhr/?page=2');
4         xhr.send()
5     }

虽然get请求可以传递变量,但是下面的send方法不能去掉。

1 def xmlhr(request):
2     if request.method=='GET':
3         page=request.GET.get('page')
4         print(page)
5         return HttpResponse('ok')
6 
7 2

post请求:

js

 1     function Xhr(ths){
 2         var xhr= new XMLHttpRequest();
 3         xhr.open('post','/xmlhr/');
 4         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
 5         xhr.send('k1=v1;k2=v2');
 6         xhr.onreadystatechange=function () {
 7             if (xhr.readyState == 4) {
 8                 console.log(xhr.responseText)
 9             }
10         }
11     }

 

注意:

  • 由于get请求的时候,不需要设置请求头,post需要设置请求头。
  • 当我们发送多个数据的时候,形式需要这样:'k1=v1;k2=v2;' 分号;隔开而且不能写多个send(string) 发送数据。
  • xhr.onreadystatechange=function (){xxx}当onreadystatechange发生变化的时候自动执行后面的匿名函数。当readState等于4的时候,说明数据传输完成,我们接收数据。如果不写xhr.onreadystatechange=function (){xxx}的话,我们接收的数据有可能是空。因为我们不能确定什么时候数据传输完成。

 FormData对象:

我们可以通过FormData对象来往后端传送相应的键值 类型于form表当传递键值对。

 1     function Xhr(ths){
 2         var xhr= new XMLHttpRequest();
 3         xhr.open('post','/xmlhr/');
 4 {#        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');#}
 5 {#        xhr.send('k1=v1;k2=v2');#}
 6         var form= new FormData();
 7         form.append('k1','v1');
 8         form.append('k2','v2');
 9         xhr.send(form);
10         xhr.onreadystatechange=function () {
11             if (xhr.readyState == 4) {
12                 console.log(xhr.responseText)
13             }
14         }
15     }

 

注意:

  •  我们在传递FormData的时候,不需要设置请求头。append的时候逗号隔开的2个字符串!

 三:基于原生ajax上传文件:

原理:通过DOM获取文件对象:    以及通过FormData把文件对象加入上传列表中(append)来上传到服务器端。

html:

1 <form action="/upload/" method="post" enctype="multipart/form-data">
2  <div>
3      <input type="file" name="img" value="上传文件" id="img" />
4  </div>
5  <div>
6      <a style="background-color: dodgerblue;cursor: pointer;display: inline-block;margin-top: 8px"
7      οnclick="Xhr(this)" >提交文件</a>
8  </div>
9 </form> 

js:

 1     function Xhr(ths){
 2         var xhr= new XMLHttpRequest();
 3         xhr.open('post','/xmlhr/');
 4         var form= new FormData();
 5         file_obj=document.getElementById('img').files[0];
 6         form.append('img',file_obj);##追append的时候key,value形式。key是后台获取文件对象的。
 7         xhr.send(form);
 8         xhr.onreadystatechange=function () {
 9             if (xhr.readyState == 4) {
10                 console.log(xhr.responseText)
11             }
12         }
13     } 

python:

1 def xmlhr(request):
2     if request.method=='POST':
3         img=request.FILES.get('img')
4         print(img.name)
5         f=open(os.path.join('static',img.name),'wb')
6         for chunk in img.chunks():
7             f.write(chunk)
8         f.close()
9         return HttpResponse('ok')#需要注意原生ajax和jqurey的ajax 返回都是字符串。

 三:基于jquery版本的上传文件:

基于jquey上传文件,和原生ajax上传文件的原理都是一样,底层都是调用模块XmlHttpRequest模块来实现文件上传,只不过jquery的ajax,的实现底层是由send()发送"k1=v1;k2=v2"的形式完成。在发送的时候,还需要设置请求头,告诉接收端该如何处理我们的发送的数据。

我们想用jquery的ajax上传文件的话,需要用FormData来实现。

所以:基于jquey的上传文件需要解决如下问题:

  • 告诉底层xmlhttprequest 不要把:data:{‘k1’:'v1'} 转换成:send('k1=v1;')即:processData:false。告诉xmlhttprequest不要处理我们的数据。
  • 告诉底层xmlhttprequest 不要设置请求头。contentType:false
  • 然后创建formData对象进行文件的对象的获取以及发送。

jquery:

 1     function Xhr_Jquery(ths){
 2         var form= new FormData();
 3         var file_obj=$('#img')[0].files[0];//获取文件对象。
 4         form.append('img',file_obj);//注意是key  value形式。
 5         $.ajax({
 6             url:'/xmlhr/',
 7             type:'post',
 8             data:form,
 9             processData:false,//对我们post数据不做处理。
10             contentType:false,//在post的时候 不设置请求头。
11             success:function(data){
12                 console.log(data)
13             }
14         });
15     }

 

    

注意:

  • 无论是原生ajax还是jquery ajax上传文件的本质原理是一样的。
  • 这2种方法 有个弊端浏览器的兼容性:都不支持IE浏览器。因为FormData在IE中不支持。

四: iframe 上传文件,用"伪ajax"提交数据,支持所有的浏览器

原理:利用form表单,在input标签提交的时候,把from的表单提交的通道转到iframe的,通过 iframe 通道提交到后台,然后隐藏iframe标签,达到当前的页面不刷新。

html:

1 <form id="fo" name="fo1"  action="/upload/" method="post" enctype="multipart/form-data">
2  <div>
3      <input type="file" name="img" value="上传文件" id="img" />
4  </div>
5  <div>
6      <iframe src="" style="display: none" name="iframe" ></iframe>
7      <input type="submit" value="iframe提交" οnclick="Iframe()" />
8  </div>
9 </form>

 

js:

1     function Iframe(){
2         document.getElementById('fo').target="iframe";//注意在dom中 没有#fo 直接id名字。
3         document.getElementById('fo').submit()
4     }

 

利用iframe的通道,上传我们的文件。通过设置 target的属性,等于iframe的name属性。来改变form提交的通道。通过js来实现form的提交。iframe只是给咱们提供一个通道,并没有其他作用。所以最后我们隐藏的iframe标签既可。

注意:

  • document.getElementById('fo')没有#
  • 注意target是iframe的name属性不是id属性值。如果是id属性的话,会导致跳转问题。

上面只是操作文件上传到后台,但是我们需要知道是否上传成功。后台代码如下:

1 def xmlhr(request):
2     if request.method=='POST':
3         img=request.FILES.get('img')
4         print(img.name)
5         f=open(os.path.join('static',img.name),'wb')
6         for chunk in img.chunks():
7             f.write(chunk)
8         f.close()
9         return HttpResponse('ok')

 

后台给前端传来字符串ok,ok嵌套在iframe标签内:

这个时候我们需要绑定时间来获取这个这个返回结果,因为frame传到后台之后,后台返回结果,也就是说当frame标签加载完成(onload)之后才有这个结果。所以绑定事件为:onload

1     function Iframe(){
2         document.getElementById('up_fr').οnlοad=callback;//绑定回调函数。
3         document.getElementById('fo').target="iframe";
4         document.getElementById('fo').submit()
5     }
6     function callback(){
7         var content=$('#up_fr').contents().find('body').text();
8         console.log(content)
9     }

 

当我们上传文件的时候,没有点击任何按钮的时候,就直接上传到服务器(抽屉例子),实现这个功能需要绑定给input标签绑定onchange事件。

还有我们上传完文件我们需要预览 这个可以直接添加img标签既可解决。

后端只是传给我们简单数据。我们可以顶一个json格式{status:False,data:None,error:None}来标记我们是否上传成功。

完整版:

实现代码:

js

 1     function Iframe(){
 2         document.getElementById('up_fr').οnlοad=callback;
 3         document.getElementById('fo').target="iframe";
 4         document.getElementById('fo').submit()
 5     }
 6     function callback(){
 7         var content=$('#up_fr').contents().find('body').text();
 8         var con=JSON.parse(content);
 9         console.log(con);
10         if (con.status){
11             var tag=document.createElement('img');
12             tag.src='/'+con.data;##注意回传的数据中文件的路径:static\1.jpg少一个/
13             tag.className='img';
14             $('.view').append(tag)
15         }
16     }

 需要注意的:

content=$('#up_fr').contents().find('body').text();

中的contents,iframe标签回调完毕的时候,会在现有的文档内嵌入一个文档,如果想要获取内部的文档的内容需要用:contents()函数来实现。

html:

 1 <form id="fo" name="fo1"  action="/upload/" method="post" enctype="multipart/form-data">
 2  <div>
 3      <input type="file" name="img" value="上传文件" id="img" οnchange="Iframe()" />
 4  </div>
 5  <div>
 6      <iframe src="" style="display: none" id="up_fr" name="iframe" ></iframe>
 7 {#     <input type="submit" value="iframe提交" οnclick="Iframe()" />#}
 8  </div>
 9 <div class="view">
10 
11 </div>
12 </form>

后端python代码:

 1 def upload(request):
 2     STATUS={'status':False,'data':None,'error':None}
 3     if request.method=='POST':
 4         try:
 5             img=request.FILES.get('img')
 6             file_path=os.path.join('static',img.name)
 7             f=open(file_path,'wb')
 8             for chunk in img.chunks():
 9                 f.write(chunk)
10             f.close()
11             STATUS['status']=True
12             STATUS['data']=file_path
13             back_data=json.dumps(STATUS)
14             return HttpResponse(back_data)
15         except Exception as e:
16             STATUS['status']=False
17             STATUS['error']=e
18             back_data=json.dumps(STATUS)
19             return HttpResponse(back_data)
20     return render(request,'upload.html')

 

转载于:https://www.cnblogs.com/evilliu/p/5908163.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值