使用input type="file",來上傳一個文件。注意:form表單必須添加屬性enctype="multipart/form-data"css
在views.py視圖函數中,獲取文件對象,必須使用request.FILES.gethtml
新建項目upload,在項目中新建static文件夾,在文件夾裏面建立upload目錄,用來保存上傳的文件。jquery
修改settings.py,定義static路徑,在最後面加上如下代碼。記住下面的等號後面是一個元組,因此必定要加一個英文的逗號‘,’,等號後面也可使用列表,這個就不須要加逗號了ajax
STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), )
在upload/urls.py,增長路由算法
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^uploading_img/', views.uploading_img, name='uploading_img'), ]
在app01/views.py中的代碼django
from django.shortcuts import render, HttpResponse, redirect from uploading import settings import os # Create your views here. def uploading_img(request): if request.method == 'POST': user = request.POST.get('user') avatar = request.FILES.get('avatar') print(user) print('avatar', avatar, type(avatar)) print(avatar.__dict__) # 查看對象的全部屬性 # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name) # 必須以二進制的方式打開,存儲圖片 with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功') return render(request, 'upload_img.html')
在templates下建立一個upload_img.htmljson
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {#上傳文件必須指定enctype="multipart/form-data"#} <form method="post" enctype="multipart/form-data"> {% csrf_token %} <lable>上傳圖片</lable> <input type="file" name="avatar"><br> <lable>用戶名</lable> <input type="text" name="user"> <input type="submit" value="提交"> </form> </body> </html>
上面用的是原始的上傳按鈕要是很差看可使用下面的bootstrap
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> </head> <body> {#上傳文件必須指定enctype="multipart/form-data"#} <form method="post" enctype="multipart/form-data"> {% csrf_token %} <label for="excelFile"><span class="btn btn-success">上傳</span></label> <input name="avatar" type="file" id="excelFile" style="display: none"> <div> <lable>用戶名:</lable> <input type="text" name="user"> <input type="submit" value="提交"> </div> </form> </body> </html>
這樣就完成了一個簡單的上傳圖片。結果我就不演示了瀏覽器
網上全部的好看的上傳按鈕都是通過修飾的。安全
下面就是一個修飾後的按鈕,簡單的修飾。能夠自定義大小
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {#上傳文件必須指定enctype="multipart/form-data"#} <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;"> <i class="fa fa-cloud-upload" aria-hidden="true"></i> <span>點擊上傳文件</span> </div> {# opacity表示設置透明度,0表示徹底透明 #} <input name="customer_excel" 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> <lable>用戶名:</lable> <input type="text" name="user"> <input type="submit" value="提交"> </div> </form> <script src="/static/js/jquery.min.js"></script> <script> $(function () { {#當元素的值發生改變時,會發生 change 事件#} $('#excelFile').change(function (e) { /* event.currentTarget 屬性是在事件冒泡階段內的當前 DOM 元素,一般等於 this :file 選擇器選取帶有 type=file 的 input 元素 0表示獲取第一個元素,name表示獲取文件名 */ var fileName = e.currentTarget.files[0].name; /*prev() 得到匹配元素集合中每一個元素緊鄰的前一個同胞元素,經過選擇器進行篩選是可選的 $(this)表示上傳控件,那麼它前一個同胞元素爲div style="position:... find() 方法得到當前元素集合中每一個元素的後代,經過選擇器、jQuery 對象或元素來篩選。 text() 方法方法設置或返回被選元素的文本內容 */ $(this).prev().find('span').text(fileName); }) }) </script> </body> </html>
通常作上傳頭像功能,會有一個預覽效果。總共有4種方法:
前2種,是在瀏覽器端能夠作圖片預覽,沒有上傳圖片到服務器!
後2種,圖片須要上傳到服務器。
兼容性效果對比: iframe > FormData > FileReader -> createObjectURL
iframe的兼容性是最好的,即便ie6也支持。FormData對瀏覽器,有必定要求
參考兼容性:
實例:
urls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^upload_img/', views.upload_img, name='upload_img'), url(r'^upload_avatar/', views.upload_avatar, name='upload_avatar'), ]
views.py的代碼,在static裏面傳建一個upload_avatar的目錄用來存儲上傳的頭像
from django.shortcuts import render, HttpResponse, redirect from uploading import settings import os # Create your views here. def upload_img(request): if request.method == 'POST': user = request.POST.get('user') avatar = request.FILES.get('avatar') print(user) print('avatar', avatar, type(avatar)) print(avatar.__dict__) # 查看對象的全部屬性 # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name) # 必須以二進制的方式打開,存儲圖片 with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功') return render(request, 'upload_img.html') def upload_avatar(request): if request.method == "GET": return render(request, 'upload_avatar.html') user = request.POST.get('user') avatar = request.FILES.get('avatar') # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, "static", "upload_avatar", avatar.name) with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功')
建立一個upload_avatar.html 注意:不是全部的瀏覽器都支持createObjectURL,在這裏要注意咱們放默認圖標的位置要在static裏面要存儲。
<!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="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/images/default.png"> <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg" name="avatar" type="file" class="img-file"/> </div> <div>點擊圖片更換(<a href="#">撤銷</a>)</div> <lable>用戶名:</lable> <input type="text" name="user"> <input type="submit" value="提交"> </form> <script src="/static/js/jquery.min.js"></script> <script> {#瀏覽器加載資源完成後#} $(function () { {#執行函數#} bindChangeAvatar1(); }); function bindChangeAvatar1() { {#綁定change事件#} $('#avatarImg').change(function () { {#$(this)表示input上傳控件,0表示第一個元素#} {#files[0] 獲取file input中的File對象#} var file_obj = $(this)[0].files[0]; console.log(file_obj); {#經過createObjectURL讀取對象,生成url#} var blob = window.URL.createObjectURL(file_obj); {#修改src的值#} document.getElementById('previewImg').src = blob; {#load() 當資源加載完成以後纔會執行#} $('#previewImg').load(function () { {#revokeObjectURL釋放對象#} window.URL.revokeObjectURL(blob); }) }) } </script> </body> </html>
實例:
修改upload_file.html裏面的js代碼便可,readAsDataURL也有兼容問題
<!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="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/images/default.png"> <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg" name="avatar" type="file" class="img-file"/> </div> <div>點擊圖片更換(<a href="#">撤銷</a>)</div> <lable>用戶名:</lable> <input type="text" name="user"> <input type="submit" value="提交"> </form> <script src="/static/js/jquery.min.js"></script> <script> $(function () { bindChangeAvatar2(); }); function bindChangeAvatar2() { $('#avatarImg').change(function () { var file_obj = $(this)[0].files[0]; //使用fileReader對文件對象進行操做 var reader = new FileReader(); //readAsDataURL 將文件讀取爲 DataURL reader.readAsDataURL(file_obj); //onload 事件會在頁面或圖像加載完成後當即發生 reader.onload = function (e) { // 修改src屬性 $('#previewImg')[0].src = this.result; }; }) } </script> </body> </html>
上面的保存圖片方式有問題,由於用戶上傳的圖片,可能會重名。爲了解決這個問題,須要使用uuid模塊。
uuid
UUID是128位的全局惟一標識符,一般由32字節的字符串表示。
它能夠保證時間和空間的惟一性,也稱爲GUID,全稱爲:
UUID —— Universally Unique IDentifier。Python 中叫 UUID
它經過MAC地址、時間戳、命名空間、隨機數、僞隨機數來保證生成ID的惟一性。
UUID主要有五個算法,也就是五種方法來實現:
一、uuid1()——基於時間戳 由MAC地址、當前時間戳、隨機數生成。能夠保證全球範圍內的惟一性, 但MAC的使用同時帶來安全性問題,局域網中可使用IP來代替MAC。 二、uuid2()——基於分佈式計算環境DCE(Python中沒有這個函數) 算法與uuid1相同,不一樣的是把時間戳的前4位置換爲POSIX的UID。 實際中不多用到該方法。 三、uuid3()——基於名字的MD5散列值 經過計算名字和命名空間的MD5散列值獲得,保證了同一命名空間中不一樣名字的惟一性, 和不一樣命名空間的惟一性,但同一命名空間的同一名字生成相同的uuid。 四、uuid4()——基於隨機數 由僞隨機數獲得,有必定的重複機率,該機率能夠計算出來。 五、uuid5()——基於名字的SHA-1散列值 算法與uuid3相同,不一樣的是使用 Secure Hash Algorithm 1 算法
使用方面:
首先,Python中沒有基於DCE的,因此uuid2能夠忽略; 其次,uuid4存在機率性重複,由無映射性,最好不用; 再次,若在Global的分佈式計算環境下,最好用uuid1; 最後,如有名字的惟一性要求,最好用uuid3或uuid5。
如何使用uuid
import uuid name = "test_name" print(uuid.uuid1()) # 帶參的方法參見Python Doc print(uuid.uuid3(uuid.NAMESPACE_DNS, name)) print(uuid.uuid4()) print(uuid.uuid5(uuid.NAMESPACE_DNS, name))
修改upload_file視圖函數,使用uuid,完整代碼以下:
from django.shortcuts import render, HttpResponse, redirect from uploading import settings import os import uuid import json # Create your views here. def upload_img(request): if request.method == 'POST': user = request.POST.get('user') avatar = request.FILES.get('avatar') print(user) print('avatar', avatar, type(avatar)) print(avatar.__dict__) # 查看對象的全部屬性 # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name) # 必須以二進制的方式打開,存儲圖片 with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功') return render(request, 'upload_img.html') def upload_avatar(request): if request.method == "GET": return render(request, 'upload_avatar.html') user = request.POST.get('user') avatar = request.FILES.get('avatar') # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, "static", "upload_avatar", avatar.name) with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功') def form_data_upload(request): """ ajax上傳文件 :param request: :return: """ img_upload = request.FILES.get('img_upload') # 獲取文件對象 # 生成隨機文件名 file_name = str(uuid.uuid4()) + "." + img_upload.name.rsplit('.', maxsplit=1)[1] # 文件保存到static下的images目錄 img_file_path = os.path.join('static', 'images', file_name) with open(img_file_path, 'wb') as f: # 寫入問題 for line in img_upload.chunks(): f.write(line) # 由於ajax發送的圖片路徑的static前面沒有帶/,因此這裏要拼接一下 return HttpResponse(os.path.join("/", img_file_path))
當咱們訪問http://127.0.0.1:8000/upload_avatar/,點擊更換圖片的時候換了一個圖片就會把換了的圖片傳到後臺並保存。
查看/static/images下多了一個fc2c2de5-41b7-4d30-ba07-ebeaee1d53af.jpg的文件
注意:form_data_upload和upload_file雖然都處理了POST數據。可是它們接收的數據是不同的!
用戶點擊上傳時,走form_data_upload視圖函數,將圖片上傳到服務器
用戶點擊提交時,將服務器的圖片url地址和用戶名提交給upload_file視圖函數。
爲何呢?由於用戶點擊上傳時,已經將圖片上傳到服務器了。因此點擊提交按鈕時,須要再將圖片上傳一次。
只須要將圖片的url地址傳給服務器便可!
全部瀏覽器都支持 <iframe> 標籤,它是兼容性最好的一種方式
iframe 元素會建立包含另一個文檔的內聯框架(即行內框架)
舉例:內嵌汽車之家
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <iframe style="width: 960px;height: 800px;" src="https://www.autohome.com.cn/beijing/" frameborder="0"></iframe> </body> </html>
用瀏覽器打開就會直接打開汽車之家
舉例2:輸入什麼地址,就跳轉什麼地址
修改iframe.html,增長一個輸入框,加入一段js代碼。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="text" id="addr"> <input type="submit" onclick="changeUrl()" value="訪問"> <iframe id="ifr" style="width: 960px;height: 800px;" src="https://www.autohome.com.cn/beijing/" frameborder="0"></iframe> <script> function changeUrl() { //獲取輸入框的值 var addr = document.getElementById("addr").value; //修改iframe的src的值 document.getElementById("ifr").src = addr; } </script> </body> </html>
會在汽車之家上面出現一個訪問框只要輸入想要的網址就會跳到對應的網站
注意:總體頁面並無刷新,只是iframe裏面刷新了!
它有2個應用場景:
1. iframe標籤
能夠修改src,且頁面不刷新
2. form表單
提交表單數據,但刷新數據
使用在上傳頭像:
urls.py,增長2條路由
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^upload_img/$', views.upload_img, name='upload_img'), url(r'^upload_avatar/$', views.upload_avatar, name='upload_avatar'), url(r'^form_data_upload/$', views.form_data_upload, name='form_data_upload'), url(r'^iframe_upload_img/$', views.iframe_upload_img, name='iframe_upload_img'), url(r'^upload_iframe/$', views.upload_iframe, name='upload_iframe'), ]
修改views.py,增長視圖函數,在最後增長2個視圖
from django.shortcuts import render, HttpResponse, redirect from uploading import settings import os import uuid import json # Create your views here. def upload_img(request): if request.method == 'POST': user = request.POST.get('user') avatar = request.FILES.get('avatar') print(user) print('avatar', avatar, type(avatar)) print(avatar.__dict__) # 查看對象的全部屬性 # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name) # 必須以二進制的方式打開,存儲圖片 with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功') return render(request, 'upload_img.html') def upload_avatar(request): if request.method == "GET": return render(request, 'upload_avatar.html') user = request.POST.get('user') avatar = request.FILES.get('avatar') # 文件存儲的絕對路徑 path = os.path.join(settings.BASE_DIR, "static", "upload_avatar", avatar.name) with open(path, 'wb') as f: for line in avatar.chunks(): f.write(line) return HttpResponse('上傳成功') def form_data_upload(request): """ ajax上傳文件 :param request: :return: """ img_upload = request.FILES.get('img_upload') # 獲取文件對象 # 生成隨機文件名 file_name = str(uuid.uuid4()) + "." + img_upload.name.rsplit('.', maxsplit=1)[1] # 文件保存到static下的images目錄 img_file_path = os.path.join('static', 'images', file_name) with open(img_file_path, 'wb') as f: # 寫入問題 for line in img_upload.chunks(): f.write(line) # 由於ajax發送的圖片路徑的static前面沒有帶/,因此這裏要拼接一下 return HttpResponse(os.path.join("/", img_file_path)) def iframe_upload_img(request): if request.method == "GET": return render(request, 'iframe_upload_img.html') USER_LIST = [] # 空列表 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 HttpResponse("上傳成功") def upload_iframe(request): # iframe post提交 ret = {'status': True, 'data': None} try: avatar = request.FILES.get('avatar') file_name = str(uuid.uuid4()) + "." + avatar.name.rsplit('.', maxsplit=1)[1] img_file_path = os.path.join('static', 'upload', 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))
增長iframe_upload_img.html
<!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 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/images/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="submit" value="提交"> </form> <script src="/static/js/jquery.min.js"></script> <script> $(function () { bindChangeAvatar4(); }); function bindChangeAvatar4() { $('#avatar').change(function () { //parent該變量指的是包含當前分割窗口的父窗口 $(this).parent().submit(); //onload 事件會在頁面或圖像加載完成後當即發生 $('#ifr')[0].onload = function (){ //獲取post返回值,好比{"status": true, "data": "/static\\upload\\bc72823e-b274-4a76-8ec2-af844a738959.jpg"} var iframeContents = $('#ifr')[0].contentWindow.document.body.innerText; console.log(iframeContents); //反向序列化數據 iframeContents = JSON.parse(iframeContents); if (iframeContents.status) { //修改圖片的src屬性 $('#prevImg').attr('src', iframeContents.data); //修改隱藏輸入框的值 $('#formAvatar').val(iframeContents.data); } } }) } </script> </body> </html>
訪問url: http://127.0.0.1:8000/iframe_upload_img/
當咱們換一個頭像的時候,點擊提交咱們會在upload下面看見新添加的圖片。
總結:
1. 在瀏覽器端能夠作圖片預覽
- createObjectURL
- FileReader
2. 使用Ajax上傳文件:
- FormData對象
3. 僞ajax上傳文件:
- iframe
- form
4. 圖片預覽
本質就是修改img的src數據
5. 使用場景:
a. 文件上傳
- FormData對象
- iframe
b. 圖片預覽
- createObjectURL、FileReader
- iframe
文件上傳場景:
假如是政府,或者傳統企業,使用最後一種
假如是互聯網企業,使用第3種!