背景
某运维平台有一个业务需求, 需要周期性对某个导出的文件进行透视分析. 通过在现有平台上添加上传文件功能, 服务器后台收到文件后完成透视分析流程, 跳转到图表等形式的报告页.
本文介绍如何利用 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 上传文件
隐藏 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>
效果如下:
使用超链接代替 input
简化 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 函数中截取上传路径中分隔符的最后一部分作为文件名参数传递给后台, 比如在 Stack Overflow 上某回答中的代码如下:
- 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
来源: http://www.jianshu.com/p/4e6773f59067