Django的AJAX實現之檔案上傳
Uploadify元件上傳檔案很酷,可以實現檔案進度上傳,而且可以批量上傳各種檔案。好處還很多,具體詳情登到官網看看文件瞭解吧。在同類元件中,Uploadify做的也很出色。打算在Django中用它,兩個東西結合使用,也算簡單,但有些細節需要記下來,以便以後重用。
這次只說上傳圖片部分,至於上傳檔案,其實可以照貓畫虎,而且來得會簡單些,只是python程式後端寫法的區別而已,前端程式碼Uploadify一律平等對待,圖片也是檔案一種特例罷了。
Django使用Uploadify元件實現圖片上傳,可以分為兩個大步驟。
一:前端引用Uploadify所需要的類庫和指令碼樣式。
Uploadify會用到JQuery類庫,還有自己的幾個指令碼和樣式檔案,搭配好了Django的靜態檔案,讓Django正確解析靜態檔案,就算成功一半了,靜態檔案的配置參考先前的部落格:《
靜態檔案我們統一存放在根目錄的site_media資料夾下,到官網http://www.uploadify.com/下載Uploadify-2.14元件,放在site_media下的plugin,隨意起名:uploadify_214,再新建個檔案下upload,來存放上傳的圖片。
前端樣式指令碼引用程式碼:
- <linkhref="/site_media/plugin/uploadify_214/uploadify.css"type="text/css"rel="stylesheet"/>
-
<scripttype="text/javascript"src="/site_media/js/jquery.js"
- <scripttype="text/javascript"src="/site_media/plugin/uploadify_214/swfobject.js"></script>
- <scripttype="text/javascript"src="/site_media/plugin/uploadify_214/jquery.uploadify.v2.1.4.min.js"></script>
引用檔案的路徑算是很重要,具體靜態配置決定這些。首先引用Uploadify的樣式檔案,然後就是先引用JQuery類庫,再引用Uploadify自身指令碼swfobject.js和jquery.uploadify.v2.1.4.min.js
Uploadify元件初始化程式碼:
- <script type="text/javascript">
- $(document).ready(function() {
- $('#file_upload').uploadify({
- 'uploader' : '/site_media/plugin/uploadify_214/uploadify.swf',
- 'script' : '{%url uploadify_script%}',
- 'cancelImg' : '/site_media/plugin/uploadify_214/cancel.png',
- 'folder' : '/upload',
- 'auto' : false,//
- 'multi': true,//設定可以上傳多個檔案
- 'queueSizeLimit':20,//設定可以同時20個檔案
- 'removeCompleted':false,//
- 'sizeLimit':10240000,//設定上傳檔案大小單位kb
- 'fileExt':'*.jpg;*.gif;*.png',//設定上傳檔案型別為常用圖片格式
- 'fileDesc':'Image Files',
- 'onInit': function () {},
- 'onError' : function (event,ID,fileObj,errorObj) {
- $('#id_span_msg').html("上傳失敗,錯誤碼:"+errorObj.type+" "+errorObj.info);
- },
- 'onSelect': function (e, queueId, fileObj) {
- $('#id_span_msg').html("");
- },
- 'onAllComplete': function (event, data) {
- if(data.filesUploaded>=1){
- $('#id_span_msg').html("上傳成功!");
- }
- }
- });
- });
- </script>
初始化指令碼,有幾個關鍵的引數需要說明一下:
uploader是元件需要flash編譯檔案,裡面封裝了Uploadify核心的處理程式。
script是後端上傳檔案程式的url,這個是後面說的,需要自己寫。
folder是上傳檔案的目錄,這裡我們不計劃使用它,隨便寫一個充數。
前端html程式碼
- <h1>Uploadify元件上傳方式</h1>
- <divclass="demo-box">
- <inputid="file_upload"type="file"name="Filedata">
- <divid="file_uploadQueue"class="uploadifyQueue"></div>
- <p><ahref="javascript:$('#file_upload').uploadifyUpload()">上傳圖片</a>
- <ahref="javascript:$('#file_upload').uploadifyClearQueue()">取消上傳</a>
- </p>
- <p><spanid="id_span_msg"></span></p>
- </div>
二:寫好後端圖片上傳的方法。
如果剛開始就把寫好的上傳程式和Uploadify結合,也許不是很明智的做法,因為過程中遇到問題,我們不很確定是後端程式的bug還是Uploadify的配置錯誤,所以建議先把寫好的後端上傳程式,用傳統的上傳方式,去測試,把程式除錯好了,再和Uploadify結合,這樣就會很清楚是那塊出現問題了。
所以我們先寫個通用的上傳函式_upload,用傳統的上傳方式測試它,該函式:
- def _upload(file):
- '''''圖片上傳函式'''
- if file:
- path=os.path.join(settings.MEDIA_ROOT,'upload')
- file_name=str(uuid.uuid1())+".jpg"
- path_file=os.path.join(path,file_name)
- parser = ImageFile.Parser()
- for chunk in file.chunks():
- parser.feed(chunk)
- img = parser.close()
- try:
- if img.mode != "RGB":
- img = img.convert("RGB")
- img.save(path_file, 'jpeg',quality=100)
- except:
- returnFalse
- returnTrue
- returnFalse
這個程式接收一個Files物件,在記憶體裡處理儲存好圖片,程式就幾行程式碼就不解釋太多了。大體是先構造一個實體地址用於儲存圖片,再把記憶體裡的圖片資訊存入img臨時變數中,判斷圖片的模式,如果不是RGB,轉換,儲存成jpg格式,返回True,失敗返回False。
該函式測試通過了,能完成儲存圖片的使命,最後就是寫Uploadify需要的函式uploadify_script
- @csrf_exempt
- def uploadify_script(request):
- response=HttpResponse()
- response['Content-Type']="text/javascript"
- ret="0"
- file = request.FILES.get("Filedata",None)
- if file:
- if _upload(file):
- ret="1"
- ret="2"
- response.write(ret)
- return response
Uploadify使用uploadify_script函式,通過Get方式把圖片控制元件的資訊提交給該函式,函式返回"text/javascript"的內容型別,如果成功寫入字元1,否則寫入非1字元。頁面的圖片控制元件命名Filedata,Django通過file = request.FILES.get("Filedata",None)獲取控制元件的圖片資訊,如果不是空的,就傳遞給剛才說的通用函式_upload,儲存圖片。
整個過程算是完結了,過程中值得注意的:
1 常常出現IO Error,如果我們已經測試_upload和uploadify_script後端程式,他們都沒有錯誤,很多程度上是因為前端的Uploadify初始化指令碼的問題,確認Uploadify幾個關鍵的引數能不能正確解析,或者是靜態檔案配置沒成功造成的。
2 Forbidden (403)這是Django引發的,Django1.3引進了CSRF,我們需要進行一些處理,給uploadify_script一個裝飾器@csrf_exempt,記住這個很關鍵,很折騰人。
3 cannot write mode P as JPEG,這個是後端上傳程式的錯誤,是因為上傳了非jpg型別的圖片,我們需要需要轉換成RGB,再儲存,上面已經提過。