django+jquery 用post方式上傳檔案採坑記錄

dhj_tsukuba發表於2021-01-04


前言

最近用django做一個線上的語音識別demo,需要上傳語音檔案,前端是bootstrap+jquery,後端是django,在我的理解範圍內,要處理POST上來的檔案,肯定要呼叫django的檢視函式,最關鍵的是他檢視函式觸發的條件是url要發生變化,否則連 request 是什麼型別都判斷不了,更別說儲存處理了。我遇到的問題是,用POST方式提交表單後url一直沒有變化,按道理應該是要跳轉到 action 定義的url裡面,但一直沒有跳轉,所以沒法觸發檢視函式。

先直接上結論,jquery的 .ajax({ }).ajaxSubmit({ }) 都沒有用,最後還是用傳統的

<input type="submit" id="submit_btn"  style="visibility:hidden" />

解決問題,一開始是因為佈局問題不想用它,最後還是妥協一下直接把他隱藏掉。

下面分別貼出嘗試過的方案,包括 js,html和django程式碼


1. 前端佈局

按鈕組和表單, action是表單要提交到的url,也是想要跳轉的url,除了url也可以是php檔案

        <div class="btn-group-lg" align="center">
            <form method="POST" id="uploadForm" action="{% url 'form' %}" enctype="multipart/form-data">   
            	<input type="file" id="audiofile" name="audiofile" value="audiofile" style="visibility:hidden"/>
                <input type="submit" id="submit_btn" style="visibility:hidden"/>
  			</form>
             
            <button type="button" id="upload_btn" class="btn btn-default" style="width:150px; outline:0">
                <span>
                    <img src="../../static/image/upload.png" class="icon-bar" width="30">
                </span>
                Upload
            </button>            
        </div> 

2. django 設定

  • 專案url函式 urls.py
urlpatterns = [
    url(r"^demo/$", demo.views.demo, name = "demo"),
    url(r"^demo/upload/$", demo.form.upload_file, name = "form")
]
  • 主頁面檢視函式 views.py
from django.shortcuts import render
def demo(request):
        return render(request, "demo/index.html")
  • 表單處理函式(自定義)form.py
import os
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse
from django import forms
from ASR_demo import settings

class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file = forms.FileField()

def handle_uploaded_file(f):
    file_path = os.path.join(settings.MEDIA_ROOT, "uploadAudio")
    with open(file_path, 'wb') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

def upload_file(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        # if form.is_valid():
        handle_uploaded_file(request.FILES['audiofile'])
        return HttpResponseRedirect("/demo/")
    else:
        return HttpResponse(request.method) 
    return HttpResponse("OK")

很明顯,upload_file函式用來判斷request型別並且處理上傳的檔案,主頁面url是 localhost:8080/demo/, 觸發該函式的契機是url變成 localhost:8080/demo/upload/, 所以需要上傳後url立刻跳轉,注意這裡的跳轉一定不能是重定向,否則request會直接變成 GET 而不是 POST

3. JS 實現對上傳操作的控制

3.1 失敗版 .ajax

    $("#audiofile").change(function(){
    var formData = new FormData();
	file = $("#audiofile")[0].files[0];
	formData.append("file", file);
	formData.append("name", "audiofile");
	$.ajax({
        url: $("#uploadForm").attr("action"),
		type:"POST",
		data:formData,
		processData:false,
		contentType:false,
		success: function(){
            alert($("#audiofile").attr("value"));
            // window.location.replace("/demo/upload/");
			},
        error: function () {
            alert("Failed");
        }
		});
	});

用這種方法可以正確地將檔案上傳到指定url,並且看後臺日誌的時候也可以發現確實是post到了指定的url,
在這裡插入圖片描述
但瀏覽器上的url沒發生變化, django的upload_file函式根本沒法觸發,很尷尬。另外可以看到上面程式碼中用window.location強行重定向到了目標url,但request就會變成GET,同樣拿不到POST檔案。注意區分一下,這個不是所謂的跨域重定向時,POST 變成 GET,那是另外一個問題,讓django支援https的話就可以解決。而我們這裡確實是通過重定向的方式向伺服器請求了這個url,他自然就會變成GET

3.2 失敗版 .ajaxSubmit

	$("#audiofile").change(function(){
        $("#uploadForm").ajaxSubmit({
			success: function(){
               //alert($("#audiofile").attr("value"));
			},
			error: function(){
				alert("Failed");
			}
		});
	});

和前面一樣,同樣可以上傳到指定url,但實際url並沒有跳轉,也就無法觸發對應的檢視函式。

3.3 成功版 直接用submit

這裡就沒什麼程式碼了,很簡單直接 click() 就行:

 $("#audiofile").change(function() {
        $("#submit_btn").click();
    });

這樣做他後面可以直接跳轉到指定url,進而就實現了對POST請求的處理。

後來自己考慮了一下,.ajax 和 **.ajaxSubmit ** 裡面都有一個 url引數,同樣是設定要把檔案或資料上傳到哪裡,這個 url 會不會和表單中的 action 發生了衝突導致url無法正確跳轉,即便寫成一樣的也不行,不寫的話他會預設提交到當前頁面,肯定也是不行的。後面打算在試一試,總之當下的問題是暫時解決了。

相關文章