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。
总结:
- form上传文件,会刷新页面。用户交互不好。
- 你会想到:用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 状态码(整数),如:200、404... 22 23 f. String statesText 24 状态文本(字符串),如:OK、NotFound...
举例:
1 <input type="button" onclick="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 onclick="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提交" onclick="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').onload=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').onload=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" onchange="Iframe()" /> 4 </div> 5 <div> 6 <iframe src="" style="display: none" id="up_fr" name="iframe" ></iframe> 7 {# <input type="submit" value="iframe提交" onclick="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')
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)