1.上传文件按钮定制
一般文件上传的按钮都比较丑,我们可以通过下面的代码实现按钮样式的更换
views.py

def upload_file(request): if request.method=="GET": return render(request,'upload_file.html') user = request.POST.get("user") pwd = request.POST.get("pwd") # 文件名 avator = request.FILES.get("customer_file") with open(avator.name,'wb') as f: for line in avator: f.write(line) return HttpResponse('上传成功')
upload_file.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post" enctype="multipart/form-data"> {% csrf_token %} <div style="position: relative;display: inline-block;height: 50px;min-width: 300px;overflow: hidden;"> <div style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 1000;border: 1px dotted #9d9d9d;color: #9d9d9d;line-height: 50px;padding-left: 15px;"> <span>点击上传文件</span> </div> <input name="customer_file" type="file" id="excelFile" style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;background-color: #333333;z-index: 1001;opacity: 0;filter:alpha(opacity=0);"> </div> <div> <input type="text" name="user"> <input type="submit" value="提交"> </div> </form> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $(function () { $('#excelFile').change(function (e) { // e.target得到的是input标签,e.target.files得到是一串filelist, // e.target.files[0]得到的是图片的一些参数,这些参数可以根据自己的需要提取 var fileName = e.target.files[0].name; $(this).prev().find('span').text(fileName); }) }) </script> </body> </html>
# form表单的enctype设置为multipart/form-data后,表单中除了文件后台能拿到,其他值后台都拿不到。
1.1针对excel表格文件上传读取到数据库
models.py

class Excles(models.Model): user = models.CharField(max_length=36) age = models.IntegerField() email = models.EmailField() firm = models.CharField(max_length=36)
url.py

urlpatterns = [ path('admin/', admin.site.urls), path('upload/', views.upload), path('index/', views.index), ]
views.py

from django.shortcuts import render from app01.models import Excles def index(request): return render(request,'index.html') import xlrd import os def upload(request): if request.method == "GET": return render(request,'upload.html') avatar = request.FILES.get('customer_excel') print(type(avatar),avatar) # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> test.xlsx print(type(avatar.name),avatar.name) # <class 'str'> test.xlsx # 写入文件 with open(avatar.name, 'wb') as f: for line in avatar: f.write(line) files = xlrd.open_workbook(avatar.name) sheet = files.sheet_by_index(0) # 选取sheet1表格 row = sheet.nrows # 列表行数 mes_lis = [] for i in range(1,row): rows = sheet.row_values(i) # 每行数据 rows_lis = Excles(user=rows[0], age=rows[1],email=rows[2],firm=rows[3]) mes_lis.append(rows_lis) # bulk_create只能接收列表,用于批量写入数据 Excles.objects.bulk_create(mes_lis) os.remove(avatar.name) # 移除这个表格 info = Excles.objects.all() return render(request,'index.html',{'info':info})
upload.html

from django.shortcuts import render from app01.models import Excles def index(request): return render(request,'index.html') import xlrd import os def upload(request): if request.method == "GET": return render(request,'upload.html') avatar = request.FILES.get('customer_excel') print(type(avatar),avatar) # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> test.xlsx print(type(avatar.name),avatar.name) # <class 'str'> test.xlsx # 写入文件 with open(avatar.name, 'wb') as f: for line in avatar: f.write(line) files = xlrd.open_workbook(avatar.name) sheet = files.sheet_by_index(0) # 选取sheet1表格 row = sheet.nrows # 列表行数 mes_lis = [] for i in range(1,row): rows = sheet.row_values(i) # 每行数据 rows_lis = Excles(user=rows[0], age=rows[1],email=rows[2],firm=rows[3]) mes_lis.append(rows_lis) # bulk_create只能接收列表,用于批量写入数据 Excles.objects.bulk_create(mes_lis) os.remove(avatar.name) # 移除这个表格 info = Excles.objects.all() return render(request,'index.html',{'info':info})
index.html

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <link href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet"> <body> <div class="container"> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> <table class="table table-hover"> <thead> <tr> <th>序号</th> <th>用户名</th> <th>年龄</th> <th>邮箱</th> <th>公司</th> </tr> </thead> <tbody> {% for ret in info %} <tr> <td>{{ ret.pk }}</td> <td>{{ ret.user }}</td> <td>{{ ret.age }}</td> <td>{{ ret.email }}</td> <td>{{ ret.firm }}</td> </tr> {% endfor %} </table> </div> </div> </body> </html>
这个例子很简单,其中需要注意的几个点:
针对文件上传的视图函数中参数进行补充说明
avatar.name:这是一个属性,不是方法,该属性得到上传的文件名,包括后缀,如123.exe;
avatar.size:这也是一个属性,该属性得到上传文件的大小
myFile.read():从文件中读取整个上传的数据,这个方法只适合小文件;
myFile.chunks():按块返回文件,通过在for循环中进行迭代,可以将大文件按块写入到服务器中;
myFile.multiple_chunks():这个方法根据myFile的大小,返回True或者False,当myFile文件大于2.5M(默认为2.5M,可以调整)时,该方法返回True,否则返回False,因此可以根据该方法来选择选用read方法读取还是采用chunks方法:
针对bulk_create的使用说明
该方法是在django1.4版本之后才有的,针对文本文件的批量导入
create()每保存一条就执行一次SQL,而bulk_create()是执行一条SQL存入多条数据,会快很多
详细上传文件的说明请参考该技术博客
2.上传图片(针对较新版本浏览器)
上传图片和文件的区别主要在于图片上传上去,一方面要在前端显现出来,一方面还要存储在后端,而文件只是实现了单方面
案例:
默认图片(存放在/static/img路径下):
default.png
views.py

import os import uuid # 生成一串随机数,用于给每个图片起随机名字 def upload_img(request): if request.method == "GET": return render(request,'upload_img.html') avatar = request.POST.get('avatar') # 图片名 print(avatar) return HttpResponse('上传成功') # 该函数是前端ajax来响应的 def form_data_upload(request): """ ajax上传文件 :param request: :return: """ img_upload = request.FILES.get('img_upload') # 图片名 print(">>>",img_upload) # 修改后的图片名 file_name = str(uuid.uuid4()) + "." + img_upload.name.rsplit('.', maxsplit=1)[1] # 图片路径,这里我创建了一个static文件,下面有创建了一个img文件,用于放置图片 img_file_path = os.path.join('static','img', file_name) with open(img_file_path, 'wb') as f: for line in img_upload.chunks(): f.write(line) return HttpResponse(img_file_path)
模板一(createObjectURL):
upload_img.html
# URL.createObjectURL()方法会根据传入的参数创建一个指向该参数对象的URL. 这个URL的生命仅存在于它被创建的这个文档里. 新的对象URL指向执行的File对象或者是Blob对象。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL()
方法来释放
createObjectURL技术参考点击此处

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div style="height: 100px;width: 100px;padding: 2px;border: 1px solid #dddddd;position: relative;"> {# 图片存储路径#} <img style="height: 100%;width: 100%;border: 0;overflow: hidden;border-radius: 50%;" id="previewImg" {# 默认图片 #} src="/static/img/default.png"> <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg" name="avatar_img" type="file" class="img-file"/> </div> <div>点击图片更换(<a href="#">撤销</a>)</div> <form method="post" enctype="multipart/form-data"> {% csrf_token %} <div> <input type="text" name="avatar" id="avatar"> <input type="submit" value="提交"> </div> </form> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $(function () { bindChangeAvatar1(); }); function bindChangeAvatar1() { $('#avatarImg').change(function () { var file_obj = $(this)[0].files[0]; // File对象,就是一个文件,比如我用input type="file"标签来上传文件,那么里面的每个文件都是一个File对象. var blob = window.URL.createObjectURL(file_obj); console.log(blob); // Blob对象,就是二进制数据,比如通过new Blob()创建的对象就是Blob对象.又比如,在XMLHttpRequest里, // 如果指定responseType为blob,那么得到的返回值也是一个blob对象. // blob:http://127.0.0.1:8002/24b475b2-0de0-430b-a13d-e613ece37cd8 document.getElementById('previewImg').src = blob; $('#previewImg').load(function () { window.URL.revokeObjectURL(blob); }) }) } </script> </body> </html>
模板二(FileReader):
upload_img.html
# 使用FileReader对象的readAsDataURL方法可以将读取到的文件编码成Data URL。Data URL是一项特殊的技术,可以将资料(例如图片)内嵌在网页之中,不用放到外部文件。使用Data URL的好处是,您不需要额外再发出一个HTTP 请求到服务器端取得额外的资料,它适合应用在内嵌小图片。
FileReader技术参考连接点击此处

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div style="height: 100px;width: 100px;padding: 2px;border: 1px solid #dddddd;position: relative;"> {# 图片存储路径#} <img style="height: 100%;width: 100%;border: 0;overflow: hidden;border-radius: 50%;" id="previewImg" {# 默认图片 #} src="/static/img/default.png"> <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg" name="avatar_img" type="file" class="img-file"/> </div> <div>点击图片更换(<a href="#">撤销</a>)</div> <form method="post" enctype="multipart/form-data"> {% csrf_token %} <div> <input type="text" name="avatar" id="avatar"> <input type="submit" value="提交"> </div> </form> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $(function () { bindChangeAvatar2(); }); function bindChangeAvatar2() { $('#avatarImg').change(function () { var file_obj = $(this)[0].files[0]; console.log(file_obj); // 获取文件对象,当中的值以键值对的形式存储 // File(352418) {name: "django生命周期.png", lastModified: 1531814882442, // lastModifiedDate: Tue Jul 17 2018 16:08:02 GMT+0800 (中国标准时间), // webkitRelativePath: "", size: 352418, …} var reader = new FileReader(); reader.readAsDataURL(file_obj); reader.onload = function (e) { $('#previewImg')[0].src = this.result; }; }) } </script> </body> </html>
模板三(FormData,基于ajax实现文件上传):
upload_img.html
主流浏览器都开始支持一个叫做FormData的对象,有了这个FormData,可以实现利用ajax上传文件,
目前主流浏览器的较新版本都已经支持这个对象了,比如Chrome 7+、Firefox 4+、IE 10+、Opera 12+、Safari 5+
技术参考链接点击此处
案例参考链接点击此处
# 通过 FormData.append()方法赋给字段的值若是数字会被自动转换为字符(字段的值可以是一个Blob对象,一个File对象,或者一个字符串,剩下其他类型的值都会被自动转换成字符串).

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div style="height: 100px;width: 100px;padding: 2px;border: 1px solid #dddddd;position: relative;"> {# 图片存储路径#} <img style="height: 100%;width: 100%;border: 0;overflow: hidden;border-radius: 50%;" id="previewImg" {# 默认图片 #} src="/static/img/default.png"> <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg" name="avatar_img" type="file" class="img-file"/> </div> <div>点击图片更换(<a href="#">撤销</a>)</div> <form method="post" enctype="multipart/form-data"> {% csrf_token %} <div> <input type="text" name="avatar" id="avatar"> <input type="submit" value="提交"> </div> </form> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $(function () { bindChangeAvatar3(); }); function bindChangeAvatar3() { $('#avatarImg').change(function () { var file_obj = $(this)[0].files[0]; // 得到的是文件对象 console.log(file_obj); // FormData的最大优点就是可以异步上传一个二进制文件 var form = new FormData(); // FormData {} console.log(form); // 创建一个空的FormData对象,然后再用append方法逐个添加键值对 form.append('img_upload', file_obj); $.ajax({ url: '/form_data_upload/', type:'POST', data: form, processData: false, // tell jQuery not to process the data contentType: false, // tell jQuery not to set contentType success: function (arg) { // arg,即后端发送过来的完整图片路径, // static\img\53632381-9c69-4280-b8e6-e1dcc35df412.jpg console.log(arg); // 更换默认图片 $('#previewImg').attr('src',"/"+arg); // input框显示图片路径 $('#avatar').val("/"+arg); } }) }) } </script> </body> </html>
# 需要注意的是这里的模板响应了两个视图函数,一定要记得在url中配制好路径
3.上传图片(对浏览器兼容广泛)
views.py

USER_LIST = [] #通过index将列表中的数据渲染在主页 def index(request): return render(request,'index.html',{'user_list':USER_LIST}) # 获取前端传来的用户名,密码,图片名称 def iframe_upload_img(request): if request.method == "GET": return render(request,'iframe_upload_img.html') user = request.POST.get('user') pwd = request.POST.get('pwd') avatar = request.POST.get('avatar') USER_LIST.append( { 'user':user, 'pwd':pwd, 'avatar':avatar } ) # 重定向到主页 return redirect('/index/') import json import uuid # 获取随机字码 def upload_iframe(request): ret = {'status':True,'data':None} try: avatar = request.FILES.get('avatar') print(avatar) file_name = str(uuid.uuid4()) + "." + avatar.name.rsplit('.', maxsplit=1)[1] img_file_path = os.path.join('static', 'img', file_name) with open(img_file_path, 'wb') as f: for line in avatar.chunks(): f.write(line) ret['data'] = os.path.join("/",img_file_path) except Exception as e: ret['status'] = False ret['error'] = '上传失败' return HttpResponse(json.dumps(ret)) # return JsonResponse(ret)
模板四(通过iframe伪异步上传):
iframe_upload_img.thml
# 在不支持html5的前提下,ajax技术是无法实现文件的异步上传,因为ajax本质上还是js写的。js不能操作浏览器端的主机,不能操作硬盘上的文件,所以无法上传文件,这里可以通过iframe内联框架,来伪装实现无页面的跳转的异步上传
# i通过frame伪异步上传,我们在上传文件的页面内联一个框架作为表单提交后的页面,同时把这个内联框架隐藏起来,伪造异步上传

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div style="height: 100px;width: 100px;padding: 2px;border: 1px solid #dddddd;position: relative;"> {# 这里给了个iframe标签,这个标签放置路径可以局部显示这个路径画面 #} <iframe style="display: none;" id="ifr" name="fffff"></iframe> <form method="POST" action="/upload_iframe/" enctype="multipart/form-data" target="fffff"> {% csrf_token %} {# 默认图片 #} <img style="height: 100px;width: 100px;border: 0;overflow: hidden;border-radius: 50%;" id="prevImg" src="/static/img/default.png"> <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatar" name="avatar" type="file" class="img-file"/> </form> </div> <form method="post" action="/iframe_upload_img/"> {% csrf_token %} <input type="text" name="avatar" id="formAvatar" style="display: none"> <input type="text" name="user" placeholder="请输入用户名"> <input type="text" name="pwd" placeholder="请输入密码"> <input type="submit" value="提交"> </form> <script src="/static/js/jquery-3.2.1.min.js"></script> <script> $(function () { bindChangeAvatar4(); }); function bindChangeAvatar4() { $('#avatar').change(function () { // 父级是form表单 $(this).parent().submit(); // contentWindow 兼容各个浏览器,可取得子窗口的 window 对象。 $('#ifr')[0].onload = function () { // 通过这种方式向iframe页面传递参数 var iframeContents = $('#ifr')[0].contentWindow.document.body.innerText; iframeContents = JSON.parse(iframeContents); console.log(iframeContents); if (iframeContents.status) { $('#prevImg').attr('src', iframeContents.data); $('#formAvatar').val(iframeContents.data); } } }) } </script> </body> </html>
# 这里的模板对应两个form表单,分别对应两个视图函数