Ajax上傳數據與文件與後端Django數據接收——兩端引渡數據協議
日前前言
這兩天做管理臺,重寫了前端後端所有關於管理的頁面、功能。每一個管理卡片都通過jq的load()方法異步加載。這樣的方式極大的節省了管理臺的資源加載過程和時間。
但同時也引出了一些問題,當我想提交POST表單的時候,不能直接加入在html結構中直接加入{% csrf_token %}進行後端驗證,這個時候,ajax便又登場了,但由於前端發送的數據和後端接收的數據始終不同—_—!!就跟標題一樣,漫長的引渡過程,耗了我整整一天的時間才解決,接下來將詳細描述踩坑過程
一、前端——把表單的架子畫出來
<link rel="stylesheet" href="articlesImportMD.min.css">
<form class="g-body-importMD" action='#' enctype="multipart/form-data" method="post">
<h2>導入MD</h2>
<div><h3>分類選擇:</h3></div>
<div><input type="checkbox" name='category' value='2' id='s-category'>Python</div>
<div><input type="checkbox" name='category' value='3' id='s-category'>Django</div>
<div><input type="checkbox" name='category' value='4' id='s-category'>JavaScript</div>
<div><input type="checkbox" name='category' value='5' id='s-category'>Css</div>
<div><input type="checkbox" name='category' value='6' id='s-category'>Linux</div>
<div><input type="checkbox" name='category' value='7' id='s-category'>安全</div>
<div><input type="checkbox" name='category' value='8' id='s-category'>樹莓派</div>
<h3><span>導入MD文件:</span><input type="file" name='htmlF' id='s-htmlF' accept=".html"></h3>
<h3><span>導入附屬圖片:</span><input type="file" name='imgF' id='s-imgF' multiple="multiple" accept="image/*"></h3>
<button type='submit'>發佈文章</button>
<br>
<div class="g-body-importMD-status" style="display:none"></div> <!--後面顯示狀態信息使用--!>
</form>
加以了一些樣式過後差不多就是這個樣式,這步不用怎麼關心,只要有功能就好了
二、前端——如何通過js獲取表單的數據和文件對象
通過ajax發送數據和文件兩種東西的話,是不能直接通過$('elm').val()
這種方式獲取到值的。需要新構造一個FormData
然後去繼承html中的form
var formDataOld = new FormData($('.g-body-importMD')); // 繼承
// 用get方法獲取到htmlF這個FileInput中的值,注意,直接打印FormData是沒有數據的
var fileHtml = formDataOld.get('htmlF');
這裏有個特殊情況,MD文件我們只需要獲取它的文件內容,如果把他也發送給服務器必定會造成響應速度減慢和資源浪費,怎麼辦呢?這裏再新構建一個FormData
,然後一個一個的append進FormData
即可
// 自定義的表單
var formData = new FormData();
// 獲取用戶上傳文件的信息(html表單)
var formDataOld = new FormData($('.g-body-importMD'));
var fileHtml = formDataOld.get('htmlF');
// 獲取文章分類
$("input:checkbox[name=category]:checked").each(function (i) {
formData.append('categoryList',$(this).val()); // 若需要對應一個列表,那就for循環append即可
});
// 獲取文章標題(獲取的是文件名,所以清一下後綴)
var fileTitle = fileHtml.name.replace('.html', '');
// 加入所有附屬圖片到自構建表單內
var photoFiles = document.getElementById("s-imgF").files;
for(var i=0;i<photoFiles.length;i++){
formData.append('imgF',photoFiles[i]);
}
// 獲取文章內容
var reader = new FileReader();
reader.readAsText(fileHtml, 'UTF-8');
reader.onload = function () {
var fileContext = this.result;
// 新數據加入表單
formData.append('fileTitleH', fileTitle);
formData.append('fileContextH', fileContext);
};
這一系列的操作過後,我們最終得到的是一個名爲formData
的表單對象,可通過上一步的formData.get()
函數分別獲取到categoryList
、fileTitleH
、fileContextH
、imgF
這四個鍵的值,如下圖:
這裏看着可能會感覺很亂,怎麼就突然冒出來一大堆代碼了,心情就跟表情包一樣,但是實際上都是些重複的操作,複製到自己的js裏執行一下就會恍然大悟的
三、前端——如何攜帶建好的表單對象發送給後端Django
只要我們把上一步構建好的formData
對象直接加入ajax的data裏就可以了,Django在獲取的時候會直接解析這個表單
其中有一個小問題,就是Djang的csrf會返回403,詳細的解決方法可以參看之前寫過的文章Ajax常用方法與後端(Django)通信403的解決方案。
<script src="jquery.cookie.js"</script>
$.ajax({
url: '/addArticleMessy/',
type: 'POST',
processData: false, // 不處理數據(必須)
contentType: false, // 不設置內容類型(必須)
headers: {
"X-CSRFToken": $.cookie('csrftoken') // Django 403處理
},
data: formData, // 直接填入自定義表單對象
success: function (data) { // 請求完成後的處理操作
$('.g-body-importMD-status').empty();
$('.g-body-importMD-status').html('<div style="color:#19b955">【' + fileTitle + '】發佈成功!</div>');
},
error: function () {
$('.g-body-importMD-status').empty();
$('.g-body-importMD-status').html('<div style="color:#c9302c">服務器數據處理失敗,詳情查看服務器日誌</div>');
}
});
四、後端——如何接收到相應數據
這裏就不講url、model文件的設置了,後端相對前端的操作要簡便一些。
由於沒有什麼重要的點,就把整段代碼展示出來,這裏說一下大概的步驟吧:
- 使用
POST.get()
和FILEFS.getlist()
獲取數據和文件對象 - 判斷是否有存儲該篇文章的文件夾(防止報系統錯誤)
- 存儲圖片
def addHF(self):
today_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
# 獲取ajax發來的表單信息
# 一個鍵的值如果是一個列表的話,請使用getlist獲取,否則返回字節對象,我就在這兒卡了好久...
category_list = self.POST.getlist('categoryList') # 分類1/2
fileTitleH = self.POST.get('fileTitleH') # 文章標題
fileContextH = self.POST.get('fileContextH') # 文章內容
files = self.FILES.getlist('imgF') # 附屬圖片
# 信息初始化
name = fileTitleH # 文章名爲文件名
category2 = '' # 多標籤獲取與判斷
if len(category_list) == 1:
category1 = category_list[0]
else:
category1 = category_list[0]
category2 = category_list[1]
def savePhotos(): # 附屬圖片的存儲,寫成函數方便下面判斷文件夾進行調用
for f in files: # 通過圖片名進行去重操作
file_exist = os.path.exists('media/articlePhotos/' + fileTitleH + '/' + f.name)
if file_exist:
pass
else:
# 這裏一定要使用wb方式寫入,chunks獲取的數據是字節類型
with open('media/articlePhotos/' + fileTitleH + '/' + f.name,'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
if not files: # 文件夾存在判斷,防止系統錯誤
pass
else:
dir_exist = os.path.exists(r'media/articlePhotos/' + fileTitleH)
if dir_exist:
savePhotos()
else:
os.mkdir(r'media/articlePhotos/' + fileTitleH)
savePhotos()
# 創建一部分字段並存儲
addHF = Article(name=name, time=today_time, category1_id=category1, category2_id=category2)
addHF.context = context
# 存進數據庫
addHF.save()
return render(self, 'jump/manager_jump.html')
最後
到這裏,所有的代碼整合測試一下,就有一個簡單的文章發佈功能了。
後面肯定會完善整個後臺管理系統的,如果評論喜歡的人多的話,應該也會出一個相關的教程,謝謝大家!
本文作者: Messy
原文鏈接:https://www.messys.top/detail/27
版權聲明: 本博客所有文章除特別聲明外, 均採用 CC BY-NC-SA 4.0 許可協議. 轉載請註明出處!