十Django的文件上传
Posted yangzm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十Django的文件上传相关的知识,希望对你有一定的参考价值。
一、上传文件相关
- 请求头ContentType
ContentType指的是请求体的编码类型,常见的类型共有3种:
application/x-www-form-urlencoded
最常见的 POST 提交数据的方式了
浏览器的原生 form 表单,如果不设置?enctype?属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个
urlencoded是一种数据格式,
比如:
username=yang&password=123&csrfmiddlewaretoken=.......
# 这样的一种格式,类似get请求提交数据的格式# 这样在后台,就可以通过request.POST,就可以直接拿到数据了 <QueryDict:'username':['yang'],'password':['123'],......>
- multipart/form-data
专门用来传输文件的消息格式,会把文件作为片段数据发送,而不是整个文件一起发送 application/json
告诉服务端消息主体是序列化后的 JSON 字符串
之前给ajax传递数据,就是把字典写成了一个类似json类型的数据
def login(request):
...
ret = ‘"code":0,"redirect_url":"/index/"‘
return HttpResponse(ret)在不加content_type=‘application/json‘时,传递过去的就是一个字符串
def login(request):
d1 = "code":0,"redirect_url":"/index/"
import json
d1_json = json.dumps(d1)
return HttpResponse(d1_json,)success:function (res) console.log(res,typeof res); # "code": 0, "redirect_url": "/index/" string .... 这个时候,就不方便我们后续的使用,我们期望还是能拿到一个字典(自定义对象的类型)
使用content_type=‘application/json‘,ajax就会调用自己内部的反序列化机制,直接调用反序列化方法
def login(request):
d1 = "code":0,"redirect_url":"/index/"
import json
d1_json = json.dumps(d1)
return HttpResponse(d1_json,content_type=‘application/json‘)success:function (res) console.log(res,typeof res); # code: 0, redirect_url: "/index/" "object" .... 此时,ajax端不需要自己序列化,也拿到了object类型的对象,这是因为内部自动调用的
application/json 在ajax端发送数据,我们如何在视图函数中应用
$.ajax(
url:‘% url "data" %‘,
type:‘post‘,
data:JSON.stringify(k1:‘v1‘,k2:‘v2‘), # 序列化成字符串
contentType:‘application/json‘, # 指定类型
....
)3 此时再 def data(request): print(request.POST) # <QueryDict: >,拿不到数据 print(request.body) # b'"k1":"v1","k2":"v2"',要从原始的数据拿 return HttpResponse('ok') b'"k1":"v1","k2":"v2"'
- 总之:
- 在django内部,对于content-type是application/x-www-form-urlencoded的,可以解析,通过request.POST.get()就能取值
- 在django内部,对于content-type是application/json类型的,是不能解析的,需要再request.body中取原始的数据,再反序列化,才能拿到数据
- content-type就是一个数据格式,后端针对不同的数据格式进行不同的解析,默认是urlencoded的,django可以解析
JsonResponse
JsonResponse是HttpResponse的子类,专门用来生成JSON编码的响应
此时传递数据的时候,既不需要自己做序列化,也不需要指定content_type=‘application/json‘,只需要把数据直接放在JsonResponse里,就会自动帮你转换成json字符串,并且自动拼接content_type=‘application/json‘,
from django.http import JsonResponse # 需要先导入
def login(request):
d1 = "code":0,"redirect_url":"/index/"
return JsonResponse(d1)
# 注意:字典类型可以直接JsonResponse(d1),若不是字典,是列表等,就要JsonResponse(d1,safe=False)
success:function (res)
console.log(res,typeof res);
# code: 0, redirect_url: "/index/" "object"
....
此时,也仍然拿到了object类型的数据
例子:
需求:用户输入127.0.0.1:8000/home,通过路径返回home.html文件,home.html文件中用ajax从data路径得到数据,然后在自己页面显示成一个列表显示出来
# 配置路径:
url(r'^home/', views.home,name='home'),
url(r'^data/', views.data,name='data'),
# 视图views:
def home(request):
return render(request,'home.html')
def data(request):
# l1 = ['故人西辞黄鹤楼','烟花三月下扬州','孤帆远影碧空尽','唯见长江天际流']
l1 = [11,22,33,44]
return JsonResponse(l1,safe=False) # 列表要加safe
# home.html:
<ul></ul>
<script src="% static 'jquery.js' %"></script>
<script>
$.ajax(
url:'% url "data" %', # 从data路径获得数据
type:'get',
success:function (res)
console.log(res);
$.each(res,function (k,v)
# each 就是循环,k,v就是索引和值
var listr = '<li>'+ v.toString() + '</li>';
# v.toString()是把数字类型转换成字符串类型
$('ul').append(listr)
)
)
</script>
可见,ajax通过异步请求,在data路径拿到了数据组成了li标签,然后放在了ul标签下。
自动拼接content_type='application/json',可以从网页的Network下的XHR观察出来,XHR下的请求就是ajax请求
form表单上传文件
- 配置路径 url(r‘^upload/‘, views.upload,name=‘upload‘),
写html,就一个表单提交数据,文件提交的(input属性是type="file")
写视图函数
def upload(request):
if request.method == ‘GET‘:
return render(request,‘upload.html‘)
else:
print(request.POST)
# 拿到的是post请求的数据,文件相关数据去request.FILES拿
return HttpResponse(‘ok‘)打印的request.POST,数据如下: <QueryDict: 'csrfmiddlewaretoken': ['XR5FUZ8nf9KPo8uNzt8OD9q0OKmeVfuLEDVDyDlTMCrWtX0jvZ6gZcwwopucWQo8'], 'head_pic': ['111.png'], 'username': ['yangzm']>
从得到的数据可以发现,request.POST得到的文件数据就是一个名字,没有任何意义
文件的数据,都在request.FILES中
# 在form表单里,要先指定消息格式
# 默认的请求消息格式是urlencoded,携带不了文件数据
# 要指定multipart/form-data才可以携带数据
def upload(request): if request.method == 'GET': return render(request,'upload.html') else: print(request.FILES) return HttpResponse('ok') # 如果form表单没有指定文件的消息格式,接收不到数据,就得到一个空字典: <MultiValueDict: > # 此时再打印request.FILES就拿到了上传了文件的数据: <MultiValueDict: 'head_pic': [<InMemoryUploadedFile: 111.png (image/png)>]>
把得到的文件数据读取出来,并保存到本地
def upload(request):
if request.method == ‘GET‘:
return render(request,‘upload.html‘)
else:
file_obj = request.FILES.get(‘head_pic‘)
file_name = file_obj.namewith open(file_name,'wb') as f: for i in file_obj: f.write(i) return HttpResponse('ok')
以上方法上传的文件没有指定路径,就直接保存在了项目的文件夹里;此时想要把上传的文件保存在指定的文件夹中,比如项目下的statics中的img文件夹中
from ajax_pro import settings
import osdef upload(request): if request.method == 'GET': return render(request,'upload.html') else: file_obj = request.FILES.get('head_pic') file_name = file_obj.name path = os.path.join(settings.BASE_DIR,'statics','img',file_name) # 拼接路径 with open(path,'wb') as f: for i in file_obj: f.write(i) # 每次读的文件不是固定长度,每次读一行,识别符为\\r,\\n,,\\r\\n,遇到这几个符号就算是读了一行 return HttpResponse('ok')
另一种保存文件的方法:
from ajax_pro import settings
import osdef upload(request): if request.method == 'GET': return render(request,'upload.html') else: file_obj = request.FILES.get('head_pic') file_name = file_obj.name path = os.path.join(settings.BASE_DIR,'statics','img',file_name) # 拼接路径 with open(path,'wb') as f: for chunck in file_obj.chunks(): f.write(chunck) # 固定长度读取,默认一次读65536B,也就是64KB,2.5M,是一个生成器 return HttpResponse('ok')
ajax上传文件
如何获取当前文件对象的数据:$(‘#file‘)[0].files[0]
后端的读取文件并保存的代码跟form的文件上传一样
前端的html文件,用到ajax
ajax上传头像:<input type="file" id="file">
ajax用户名:<input type="text" id="uname">
<button id="btn">提交</button>
% csrf_token %
<script src="% static 'jquery.js' %"></script>
<script>
$('#btn').click(function ()
$.ajax(
url:'/upload/',
type:'post',
data:
aa: $('#uname').val(),
head_pic:$('#file')[0].files[0],
csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val()
,
success:function (res)
console.log(res)
)
)
</script>
根据以前写ajax代码的习惯,会这样写,但是会报错,因为文件上传要依靠FormData这个工具
正确写法:
ajax上传头像:<input type="file" id="file">
ajax用户名:<input type="text" id="uname">
<button id="btn">提交</button>
% csrf_token %
<script src="% static 'jquery.js' %"></script>
<script>
$('#btn').click(function ()
var formdata = new FormData();
formdata.append('aa',$('#uname').val());
formdata.append('head_pic',$('#file')[0].files[0]);
formdata.append('csrfmiddlewaretoken',$('[name=csrfmiddlewaretoken]').val());
$.ajax(
url:'/upload/',
type:'post',
data:formdata,
processData:false, //不处理数据
contentType:false, //不设置内容类型
success:function (res)
console.log(res)
)
)
</script>
注意点:
- ajax文件上传需要借助FormData,给它填键值对的方式传输数据
- data直接传formdata,但是后面要加processData:false和contentType:false
- JSON 机制
- JSON 指的是 javascript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言 *
- JSON 具有自我描述性,更易理解
json和python数据类型对比:
但是,json在转换时间类型的时候,会出现问题:
import datetime
import json
d = 'name':'yang','birthday':datetime.datetime.now()
json_str = json.dumps(d)
print(json_str)
# 报错,TypeError: Object of type 'datetime' is not JSON serializable,意思是'datetime'类型的是不能序列化的
json序列化时间日期类型的数据的方法
from datetime import date,datetime
import json
#对含有日期格式数据的json数据进行转换
class JsonCustomEncoder(json.JSONEncoder):
# 序列化的功能就是json.JSONEncoder这个类做的,因为它不能序列化时间,所以继承他的情况下进行拓展
def default(self, field):
if isinstance(field,datetime):
# 如果是datetime类型的,返回一个datetime类型的字符串格式化输出
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field,date):
# 如果是date类型的,返回一个date类型的字符串格式化输出
return field.strftime('%Y-%m-%d')
else:
# 如果不是date、datetime类型的,就用父类的方法
return json.JSONEncoder.default(self,field)
d1 = datetime.now()
json_str = json.dumps(d1,cls=JsonCustomEncoder)
print(json_str) # "2019-07-29 21:08:25"
以上是关于十Django的文件上传的主要内容,如果未能解决你的问题,请参考以下文章