最近用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代码
按钮组和表单, 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>
urlpatterns = [
url(r"^demo/$", demo.views.demo, name = "demo"),
url(r"^demo/upload/$", demo.form.upload_file, name = "form")
]
from django.shortcuts import render
def demo(request):
return render(request, "demo/index.html")
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。
$("#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。
$("#audiofile").change(function(){
$("#uploadForm").ajaxSubmit({
success: function(){
//alert($("#audiofile").attr("value"));
},
error: function(){
alert("Failed");
}
});
});
和前面一样,同样可以上传到指定url,但实际url并没有跳转,也就无法触发对应的视图函数。
这里就没什么代码了,很简单直接 click() 就行:
$("#audiofile").change(function() {
$("#submit_btn").click();
});
这样做他后面可以直接跳转到指定url,进而就实现了对POST请求的处理。
后来自己考虑了一下,.ajax 和 **.ajaxSubmit ** 里面都有一个 url参数,同样是设定要把文件或数据上传到哪里,这个 url 会不会和表单中的 action 发生了冲突导致url无法正确跳转,即便写成一样的也不行,不写的话他会默认提交到当前页面,肯定也是不行的。后面打算在试一试,总之当下的问题是暂时解决了。