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表單,分別對應兩個視圖函數