背景
某运维平台有一个业务需求,需要周期性对某个导出的文件进行透视分析。通过在现有平台上添加上传文件功能,服务器后台收到文件后完成透视分析流程,跳转到图表等形式的报告页。
本文介绍如何利用Django完成文件上传功能,并且因为统一风格和美化的关系,前台隐藏input标签。
Django部分
views.py
views中两个处理逻辑:file_upload函数负责提供上传页面(此函数正式平台不需要,后面讲到);upload负责接收文件并完成实际的文件处理,返回生成的报告。
def file_upload(request):
return render(request, 'file_upload_input_submit.html')
def upload(request):
f = request.FILES['upload_file']
filename = f.name
with open('upload\\{}'.format(filename), 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
# do something with the file uploaded
report = '<h1>UPLOADED FILE:{}</h1><h2>REPORT FROM FILE CONTENTS</h2>'.format(filename)
# render report
return render_to_response('report.html', {'report': report})
urls.py
添加两个地址路由:file_upload地址路由到views.file_upload,upload地址路由到views.upload。
from django.contrib import admin
from django.urls import path
import app.views as views
urlpatterns = [
path('admin/', admin.site.urls),
path('file_upload/', views.file_upload),
path('upload/', views.upload),
]
TEMPLATES模板
最普通的上传页(显示input)
仅完成上传功能的file_upload_input_submit.html模板,通过input按钮与用户交互,其body部分代码如下:
<body>
<form enctype="multipart/form-data" name='form' method="post" action="/upload/">{% csrf_token %}
<input type="file" name="upload_file" onChange="document.forms['form'].submit();"/>
</body>
留着input就是很丑,效果如下:

隐藏input
file_upload_hide_input_with_function.html模板中隐藏了input,通过超链接调用click_upload函数→模拟点击input→弹出文件选择对话框,选取文件→触发onChange事件→提交表单→访问upload地址,代码如下:
<head>
<title>文件上传</title>
<script>
function click_upload() {
document.getElementById('input').click();
}
</script>
</head>
<body>
<form enctype="multipart/form-data" name='form' method="post" action="/upload/">{% csrf_token %}
<input type="file" id='input' name="upload_file" onChange="document.forms['form'].submit();" style='display:none'/>
<a href='javascript:click_upload();' >UPLOAD</>
</body>
效果如下:

简化html
其实前面一个模板的function中就一行代码,如果觉得一个函数就一行代码显得比较啰嗦,可以将这行代码直接写到a标签的href中。写成函数在整个前端中可以保持代码风格统一,而嵌套在html中则比较简洁,两者没有好坏之分,视具体应用场景。
<body>
<form enctype="multipart/form-data" name='form' method="post" action="/upload/">{% csrf_token %}
<input type="file" id='input' name="upload_file" onChange="document.forms['form'].submit();" style='display:none'/>
<a href='javascript:document.getElementById("input").click();' >UPLOAD</a>
</body>
嵌入正式平台
正式平台左侧是一堆功能链接,因此不需要单独的上传页。将表单和超链接部分的代码插入平台的主页即可。

上传文件完成后跳转到报告演示页:

关于文件名
一开始为上传到服务器的文件定了一个临时的文件名,后来想使用文件原来的名字在服务器端命名。使用搜索引擎查找了一下,得到的方法是在js函数中截取上传路径中分隔符的最后一部分作为文件名参数传递给后台,比如在StackOverflow上某回答中的代码如下:
function uploadOnChange() {
var filename = this.value;
var lastIndex = filename.lastIndexOf("\\");
if (lastIndex >= 0) {
filename = filename.substring(lastIndex + 1);
}
document.getElementById('filename').value = filename;}
这也是为什么我在尝试隐藏input时首先单独写了个函数的原因(原本的函数是不止1行的)。后来翻Django文档时看到,UploadedFile 类其实是有name属性的:
name = property(_get_name, _set_name)
所以在views.upload中直接对文件调用该属性即可,没有必要用js那么麻烦。
参考资料
https://docs.djangoproject.com/en/2.1/topics/http/file-uploads/
https://docs.djangoproject.com/en/2.1/_modules/django/core/files/uploadedfile/
http://www.cnblogs.com/linxiyue/p/4038436.html
https://stackoverflow.com/questions/5480934/pass-filename-from-file-upload-to-text-field
网友评论