Django中的文件上传和原生Ajax
概述
Django中的上传有3种方案:
- form 表单常规上传,但点击提交后会自动刷新页面
- Ajax 上传,不刷新页面,(分为原生ajax上传和jQuery上传),IE7以上不兼容
- iframe 上传,兼容性没问题,也不会刷新页面
常规表单上传
这种方式有个缺点,就是会自动刷新页面
后台接收文件时,通过request.files.get('xxx')
得到文件对象,并通过对文件循环,循环对象为文件对象.chunks()写入新文件即可!后台接收文件都使用这种方式!
可以看下源码:
class InMemoryUploadedFile(UploadedFile):
"""
A file uploaded into memory (i.e. stream-to-memory).
"""
def __init__(self, file, field_name, name, content_type, size, charset, content_type_extra=None):
super(InMemoryUploadedFile, self).__init__(file, name, content_type, size, charset, content_type_extra)
self.field_name = field_name
def open(self, mode=None):
self.file.seek(0)
#这里可以看到本身是一个迭代器!
def chunks(self, chunk_size=None):
self.file.seek(0)
yield self.read()
def multiple_chunks(self, chunk_size=None):
# Since it's in memory, we'll never have multiple chunks.
return False
他的父类:
lass UploadedFile(File):
"""
A abstract uploaded file (``TemporaryUploadedFile`` and
``InMemoryUploadedFile`` are the built-in concrete subclasses).
An ``UploadedFile`` object behaves somewhat like a file object and
represents some file data that the user submitted with a form.
"""
DEFAULT_CHUNK_SIZE = 64 * 2 ** 10
def __init__(self, file=None, name=None, content_type=None, size=None, charset=None, content_type_extra=None):
super(UploadedFile, self).__init__(file, name)
self.size = size
self.content_type = content_type
self.charset = charset
self.content_type_extra = content_type_extra
def __repr__(self):
return force_str("<%s: %s (%s)>" % (
self.__class__.__name__, self.name, self.content_type))
def _get_name(self):
return self._name
def _set_name(self, name):
# Sanitize the file name so that it can't be dangerous.
if name is not None:
# Just use the basename of the file -- anything else is dangerous.
name = os.path.basename(name)
# File names longer than 255 characters can cause problems on older OSes.
if len(name) > 255:
name, ext = os.path.splitext(name)
ext = ext[:255]
name = name[:255 - len(ext)] + ext
self._name = name
name = property(_get_name, _set_name)
然后我们来看下代码吧:
HTML文件内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload</title>
</head>
<body>
<form action="/upload/" enctype="multipart/form-data" method="POST">
<input type="text" name="user"/>
<input type="file" name="img"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
views.py 后台处理:
def upload(request):
if request.method == 'POST':
user = request.POST.get('user')
img = request.FILES.get('img')
print(user, img.name)
f = open(os.path.join('static', img.name), 'wb')
for chunk in img.chunks():
f.write(chunk)
f.close()
return render(request, 'upload.html')
原生Ajax
AJAX,Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。
异步的JavaScript:
使用 【JavaScript语言】 以及 相关【浏览器提供类库】 的功能向服务端发送请求,当服务端处理完请求之后,【自动执行某个JavaScript的回调函数】。
PS:以上请求和响应的整个过程是【偷偷】进行的,页面上无任何感知。
XML
XML是一种标记语言,是Ajax在和后台交互时传输数据的格式之一
利用AJAX可以做:
1、注册时,输入用户名自动检测用户是否已经存在。
2、登陆时,提示用户名密码错误
3、删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。
Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)。
XmlHttpRequest对象介绍
XmlHttpRequest对象的主要方法:
a. void open(String method,String url,Boolen async)
用于创建请求
参数:
method: 请求方式(字符串类型),如:POST、GET、DELETE...
url: 要请求的地址(字符串类型)
async: 是否异步(布尔类型)
b. void send(String body)
用于发送请求
参数:
body: 要发送的数据(字符串类型)
c. void setRequestHeader(String header,String value)
用于设置请求头
参数:
header: 请求头的key(字符串类型)
vlaue: 请求头的value(字符串类型)
d. String getAllResponseHeaders()
获取所有响应头
返回值:
响应头数据(字符串类型)
e. String getResponseHeader(String header)
获取响应头中指定header的值
参数:
header: 响应头的key(字符串类型)
返回值:
响应头中指定的header对应的值
f. void abort()
终止请求
XmlHttpRequest对象的主要属性:
a. Number readyState
状态值(整数)
详细:
0-未初始化,尚未调用open()方法;
1-启动,调用了open()方法,未调用send()方法;
2-发送,已经调用了send()方法,未接收到响应;
3-接收,已经接收到部分响应数据;
4-完成,已经接收到全部响应数据;
b. Function onreadystatechange
当readyState的值改变时自动触发执行其对应的函数(回调函数)
c. String responseText
服务器返回的数据(字符串类型)
d. XmlDocument responseXML
服务器返回的数据(Xml对象)
e. Number states
状态码(整数),如:200、404...
f. String statesText
状态文本(字符串),如:OK、NotFound...
原生Ajax例子
我们来看个Django中Ajax的例子吧:
views.py文件:
#发送页面
def ajax(request):
import time
time = time.time()
return render(request,'ajax.html',{'time':time})
#接受数据,并返回response数据
def xhr_ajax(request):
print(request.GET)
print(request.POST)
return HttpResponse('OK')
HTML文件 ajax.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax-原生</title>
</head>
<body>
{{ time }}
<input type="button" value="XMLHttpRequest按钮" onclick="XhrAjax();"/>
<input type="button" value="XMLHttpRequest,FormData按钮" onclick="XhrAjaxForm();"/>
<script>
function XhrAjax() {
var xhr = new XMLHttpRequest();
// 如果xhr的值改变时就处罚函数
xhr.onreadystatechange = function () {
// 如果数据接受完毕,打印responseText
if (xhr.readyState == 4) {
console.log(xhr.responseText);
}
};
xhr.open('POST', '/xhr_ajax/');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
xhr.send('k1=v1;k2=v2');
}
// 使用FormData对象传送
function XhrAjaxForm() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
console.log(xhr.responseText);
}
};
// GET请求
//xhr.open('GET', '/xhr_ajax?p=123');
//xhr.send();
// POST请求
xhr.open('POST', '/xhr_ajax/');
// 设置请求头
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
var form = new FormData();
form.append('user', 'alex');
form.append('pwd', '123');
xhr.send(form);
}
</script>
</body>
</html>
例子中有两种方式:
- 原生XMLHttpRequest传送数据,send()发送的是字符串,而且必须使用
setRequestHeader
设置信息请求头 - FormData对象介质传送数据,通过append将(可以看为元组信息)信息传入,然后send(),但send对象为form对象,此种方式不需要设置信息请求头
- 特别需要注意的一点:如果使用GET方式,需要设置URL,字符串方式
Ajax上传文件
为了上传文件后不刷新页面,我们引入ajax方式来上传,请求时都是通过FormData对象封装数据,这种方式又分为两种:
- 原生Ajax方式上传
- jQuery + Ajax方式上传
views.py 配置:
def upload_ajax(request):
if request.method == 'POST':
user = request.POST.get('user')
img = request.FILES.get('img')
print(user, img.name)
f = open(os.path.join('static', img.name), 'wb')
for chunk in img.chunks():
f.write(chunk)
f.close()
return HttpResponse('ok')
return render(request, 'upload_ajax.html')
html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload</title>
</head>
<body>
// 注意form上传文件时,需要配置enctype
<form action="/upload_ajax/" enctype="multipart/form-data" method="POST">
<input type="text" name="user" id="user"/>
<input type="file" name="img" id="img"/>
</form>
<a style="cursor: pointer;display: inline-block;background-color: black;color: white" onclick="UploadFile1();">XMLHttpRequest上传</a>
<a style="cursor: pointer;display: inline-block;background-color: black;color: white" onclick="UploadFile2();">jQuery上传</a>
<script src="/static/jquery-1.12.4.js"></script>
<script>
// 原生上传
function UploadFile1() {
var form = new FormData();
form.append('user', document.getElementById('user').value);
// 得到上传图片中的第一张,使用files[下标]
var fileObj = document.getElementById('img').files[0];
form.append('img', fileObj);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4){
var data = xhr.responseText;
console.log(data);
}
};
xhr.open('POST','/upload_ajax/',true);
xhr.send(form);
}
// ajax + jQuery上传
function UploadFile2() {
var fileobj = $("#img")[0].files[0];
var form = new FormData();
form.append('img',fileobj);
form.append('user','alex');
$.ajax({
type:'POST',
url:'/upload_ajax/',
data:form,
processData:false, // 告诉jquery不转换数据
contentType:false, // 告诉jquery不设置内容格式
success:function (arg) {
console.log(arg);
}
})
}
</script>
</body>
</html>
从上面的代码来看,第二张方式要简单很多,不需要send、open,之需要按常规ajax写,然后声明不转换数据,不设置内容格式即可.
需要注意的是,上面的这两种方式,兼容性不是很好,对IE6之前的浏览器不支持,所以我们使用iframe来做.
iframe 上传
需求分析
- 异步上传
- 上传完后页面显示上传后的图片
因为iframe有内嵌页面的功能,所以可以使用iframe将上传后的图片显示,分以下步骤:
- 提交图片前将图片提交给iframe
- iframe代替本身页面发送文件给后台
- 后台返回数据提交给iframe
- 页面通过回调函数,取到后台返回的数据
- 将数据拆分,取到后台的上传后的文件目录
- 页面创建一个img的标签,添加到本页面显示图片
总结一下:页面向后台提交数据时,中间由iframe提交文件并接收后台返回的数据
views.py 后台:
def upload_iframe(request):
if request.method == 'POST':
ret = {'data':None,'status':False,'error':None}
try:
user = request.POST.get('user')
img = request.FILES.get('img')
file_path = os.path.join('static', img.name)
f = open(file_path, 'wb')
for chunk in img.chunks():
f.write(chunk)
f.close()
ret['status'] = True
ret['data'] = file_path
except Exception as e:
ret['error'] = str(e)
return HttpResponse(json.dumps(ret))
return render(request, 'upload_iframe.html')
HTML页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload_iframe</title>
<style>
/* 设置显示上传后图片的样式*/
.img{
width: 250px;
height: 250px;
}
</style>
</head>
<body>
<iframe id="my_iframe" name="my_iframe" style="display: none" src=""></iframe>
<form id="fo" method="POST" action="upload_iframe.html" enctype="multipart/form-data">
{# <input type="text" id="user" name="user"/>#}
// id 和 name 的属性必须都有
<input type="file" id="img" name="img" onchange="UploadFile3();"/>
{# <input type="submit"/>#}
</form>
<div id="container"></div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function UploadFile3() {
// 方式重复显示图片,删除之前显示的图片
$('#container').find('img').remove();
// onload事件会在页面或者图像加载完成后立即触发
document.getElementById('my_iframe').onload = callback;
// target 属性指定在何处打开表单中的 action-URL
document.getElementById('fo').target = 'my_iframe'
// 提交
document.getElementById('fo').submit();
}
function callback() {
// 使用contents(),可以将iframe中内嵌的页面HTML内容取出,取出的数据为字符串格式
var text = $('#my_iframe').contents().find('body').text();
// 字符串通过json转化为字典
var json_data = JSON.parse(text);
console.log(json_data);
if (json_data.status) {
// 创建一个img的元素
var tag = document.createElement('img');
// 字符串拼接
tag.src = '/' + json_data.data;
tag.className = 'img';
// 添加到目标元素标签内
$('#container').append(tag);
} else {
alert(json_data.error)
}
}
</script>
</body>
</html>
Django中的文件上传和原生Ajax的更多相关文章
- Day21 Django之Form文件上传、原生Ajax和实现抽屉实例
一.Form文件上传 """ Django settings for prev_chouti project. Generated by 'django-admin st ...
- python_way day21 Django文件上传Form方式提交,原生Ajax提交字符处啊,Django文件上传之原生Ajax方式、jQuery Ajax方式、iframe方式,Django验证码,抽屉示例,
python_way day21 1.Django文件上传至Form方式 2.原生Ajax文件上传提交表单 使用原生Ajax好处:不依赖jquery,在发送一个很小的文件或者字符串的时候就可以用原生A ...
- django中处理文件上传文件
1 template模版文件uploadfile.html 特别注意的是,只有当request方法是POST,且发送request的<form>有属性enctype="multi ...
- ajax多文件上传,js原生ajax请求(转)
function uploadImageFile(){ var xhr = new XMLHttpRequest(); //定义表单变量 var file = document.getElementB ...
- IIS 7 中设置文件上传大小的方法
在IIS 6.0中设置文件上传大小的方法,就是配置如下节点: <system.web> <httpRuntime maxRequestLength="1918200&quo ...
- 在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件(转)
引言 这两天沉迷了Google SketchUp,刚刚玩够,一时兴起,研究了一下WebBrowser. 我在<WebBrowser控件使用技巧分享>一文中曾谈到过“我现在可以通过WebBr ...
- PHP中,文件上传实例
PHP中,文件上传一般是通过move_uploaded_file()来实现的. bool move_uploaded_file ( string filename, string destinati ...
- MVC中的文件上传-小结
web开发中,文件的上传是非常基本功能之一. 在asp.net中,通常做法是利用webservice 来接收文件请求,这样做的好处就是全站有了一个统一的文件上传接口,并且根据网站的实际情况,可以将we ...
- ASP.NET中的文件上传大小限制的问题
一.文件大小限制的问题 首先我们来说一下如何解决ASP.NET中的文件上传大小限制的问题,我们知道在默认情况下ASP.NET的文件上传大小限制为2M,一般情况下,我们可以采用更改WEB.Config文 ...
随机推荐
- 赤池信息准则AIC,BIC
很多参数估计问题均采用似然函数作为目标函数,当训练数据足够多时,可以不断提高模型精度,但是以提高模型复杂度为代价的,同时带来一个机器学习中非常普遍的问题——过拟合.所以,模型选择问题在模型复杂度与模型 ...
- Java:JVM的内存模型
JVM内存模型 JVM内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的. 1. 堆(Heap) 堆内存是所有线程共有的,可以分为两 ...
- 「数据结构与算法之链表(Python)」(四)
什么是链表 顺序表的储存分为一体式结构和分离式结构,但总的来说存储数据的内存是一块连续的单元,每次申请前都要预估所需要的内存空间大小.这样就不能随意的增加我们需要的数据了.链接就是为了解决这个问题.它 ...
- Python中日志logging模块
# coding:utf-8 import logging import os import time class Logger(object): def __init__(self): # 创建一个 ...
- 如何使用python内置的request发送JSON格式的数据
使用步骤如下: 一.如果想发送json格式的数据,需要使用request模块中的Request类来创建对象,作为urlopen函数的参数 二.header中添加content-type为applica ...
- bzoj 3398
f[i]表示最后一个是公牛的方案数,=sigma(f[j])(j<i-k) 然后前缀和优化即可. #include <cstdio> #include <cstdlib> ...
- P3066 [USACO12DEC] 逃跑的Barn 左偏树
P3066 逃跑的Barn 左偏树 题面 题意:给出以1号点为根的一棵有根树,问每个点的子树中与它距离小于等于l的点有多少个. 注意到答案的两个性质: 一个点的所有答案一定包含在其所有儿子的答案中 如 ...
- [TJOI2013]松鼠聚会 曼哈顿距离
[TJOI2013]松鼠聚会 luogu P3964 首先容易得到两点间距离是\(max(|x_1-x_2|, |y_1-y_2|)\)(即切比雪夫距离) 然后有个套路:原\((x,y)\)求曼哈顿距 ...
- ansible user模块
查看模块的功能和选项,使用ansible-doc命令 ansible-doc options: -l #查看所有可用的模块 -m #查看模块的路径 -v #查看版本 -t TYPE #查看插件,插件: ...
- Tkinter 之Canvas画布
一.参数说明 参数 作用 background(bg) 指定 Canvas 的背景颜色 borderwidth(bd) 指定 Canvas 的边框宽度 closeenough 指定一个距离,当鼠标与画 ...