Ajax上傳數據與文件與後端Django數據接收——兩端引渡數據協議

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()函數分別獲取到categoryListfileTitleHfileContextHimgF這四個鍵的值,如下圖:
在這裏插入圖片描述


這裏看着可能會感覺很亂,怎麼就突然冒出來一大堆代碼了,心情就跟表情包一樣,但是實際上都是些重複的操作,複製到自己的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 許可協議. 轉載請註明出處!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章