文件上传

一、上传到自己的服务器:

 

使用 AdminLTE 2 网站中的 form 表单的代码:https://adminlte.io/themes/AdminLTE/pages/forms/general.html (使用其原先css样式及HTML代码,代码内容经过精简)

 

前端代码:

 

首先:一个带有输入框跟上传按钮的HTML文件(样式图如下:)

1 <div class="form-group">
2     <label for="thumbnail-form">缩略图</label>
3     <div class="input-group">
4         <input type="text" class="form-control" id="thumbnail-form" name="thumbnail">
5         <span class="input-group-btn">
6             <input type="file" class="btn btn-default" id="thumbnail-btn" value="上传文件">
7         </span>
8     </div>
9 </div>
View Code

使用 input标签 , type=file 时显示的样式;

 

将上图修改为下图的样式,使其更美观;

 1 <div class="form-group">
 2     <label for="thumbnail-form">缩略图</label>
 3     <div class="input-group">
 4         <input type="text" class="form-control" id="thumbnail-form" name="thumbnail">
 5         <span class="input-group-btn">
 6             <label class="btn btn-default btn-file">
 7                 上传图片<input  hidden type="file" class="btn btn-default" id="thumbnail-btn">
 8             </label>
 9         </span>
10     </div>
11 </div>
View Code

id = "thumbnail-form":输入框的ID;

id = "thumbnail-btn":作为隐藏起来的 file标签 文件的一个按钮ID;

使用 label标签,其中 for 中的值等于 input 的ID相当于点击“缩略图”时会默认点击下方输入框;点击“上传图片”会默认点击“type=file”的标签;二者之间存在关联;

使用 hidden 将 file标签 隐藏起来,使“上传图片”的字体将其覆盖;

 

使用js文件监听其上传时的点击事件;

 1 function News() {
 2 
 3 }
 4 
 5 News.prototype.run = function () {
 6     var self = this;
 7     self.ListenUploadFileEvent();
 8 };
 9 
10 // 监听文件上传的事件;
11 News.prototype.ListenUploadFileEvent = function () {
12     var uploadBtn = $('#thumbnail-btn');
13     uploadBtn.change(function () {
14         var file = uploadBtn[0].files[0];
15         var formData = new FormData();
16         formData.append('file', file);
17         xfzajax.post({
18             'url': '/cms/upload_file/',
19             'data': formData,
20             'processData': false,
21             'contentType': false,
22             'success': function (result) {
23                 if (result['code'] === 200){
24                     var url = result['data']['url'];
25                     var thumbnailInput = $('#thumbnail-form');
26                     thumbnailInput.val(url);
27                 }
28             }
29         })
30     });
31 };
32 
33 $(function () {
34     var news = new News();
35     news.run();
36 });
View Code

 

change:用来监听进入上传文件选定文件后点击打开按钮的事件,此处不能使用 click 点击事件;

因为 uploadBtn 得到的是一个集合,不是一个单一的对象。所以 var file = uploadBtn[0].files[0]; 中的 uploadBtn[0] 用来获取其中的唯一一个按钮,与其存储的所有文件中的第一个文件。

formData:用来存储文件;

 formData.append('file', file); :引号中的字段名需与后台通过 ‘get’ 获取文件填写的字段名保持一致;

然后通过ajax请求发送给后端服务器,跳转的 url 及其数据 formData,后两个 false  为告知上层 jquery-3.3.1.min.js 文件不对该文件的数据再次进行处理或添加其它内容,以及成功或者失败的返回内容(失败内容定义在ajax文件中);

在后端中得到的url完整路径,调用 ‘restful’ 中的 ‘result’ 函数,再将获取的 ‘url’ 的值给到前端输入框中显示。

 

ajax代码

 1 //static:xfzajax.js
 2 function getCookie(name) {
 3     var cookieValue = null;
 4     if (document.cookie && document.cookie !== '') {
 5         var cookies = document.cookie.split(';');
 6         for (var i = 0; i < cookies.length; i++) {
 7             var cookie = jQuery.trim(cookies[i]);
 8             // Does this cookie string begin with the name we want?
 9             if (cookie.substring(0, name.length + 1) === (name + '=')) {
10                 cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
11                 break;
12             }
13         }
14     }
15     return cookieValue;
16 }
17 
18 var xfzajax = {
19     'get': function (args) {
20         args['method'] = 'get';
21         this.ajax(args);
22     },
23     'post': function (args) {
24         args['method'] = 'post';
25         this._ajaxSetup();
26         this.ajax(args);
27     },
28     'ajax': function (args) {
29         var success = args['success'];
30         args['success'] = function (result) {
31             if (result['code'] === 200) {
32                 if (success) {
33                     success(result)
34                 }
35             } else {
36                 var messageObject = result['message'];
37                 if (typeof messageObject == 'string' || messageObject.constructor === String) {
38                     window.messageBox.showError(messageObject);
39                 } else {
40                     // {"password":['密码最大长度不能超过20为!','xxx'],"telephone":['xx','x']}
41                     for (var key in messageObject) {
42                         var messages = messageObject[key];
43                         var message = messages[0];
44                         window.messageBox.showError(message);
45                     }
46                 }
47                 if(success){
48                     success(result)
49                 }
50             }
51         };
52         args['fail'] = function (error) {
53             console.log(error);
54             window.showError('服务器内部错误!')
55         };
56         $.ajax(args);
57     },
58     '_ajaxSetup': function () {
59         $.ajaxSetup({
60             beforeSend: function (xhr, settings) {
61                 if (!/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type) && !this.crossDomain) {
62                     xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
63                 }
64             }
65         });
66     }
67 };
View Code

 

 

后端代码:

 

settings.py文件的设置:

1 # 在末尾添加内容;
2 
3 STATIC_URL = '/static/'
4 STATICFILES_DIRS = [
5     os.path.join(BASE_DIR,'front','dist'),
6 ]
7 
8 MEDIA_URL = '/media/'
9 MEDIA_ROOT = os.path.join(BASE_DIR,'media')
View Code

使用MEDIA_ROOTMEDIA_URL,用来指定文件上传后存储的位置,在主目录中新建‘media’文件用来存储内容;

 

文件的映射路径,与前端 ajax 内容中跳转的 url 一致。

 1 # 总的url链接;
 2 
 3 from django.urls import path,include
 4 from django.conf.urls.static import static
 5 from django.conf import settings
 6 
 7 urlpatterns = [
 8     path('cms/',include('apps.cms.urls')),
 9 ] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
10 
11 
12 # cms的url:
13 
14 from django.urls import path
15 from . import views
16 
17 app_name = 'cms'
18 
19 urlpatterns = [
20     path('upload_file/',views.upload_file,name='upload_file'),
21 ]
View Code

 

视图函数

 1 from django.views.decorators.http import require_POST, 
 2 from utils import restful
 3 from django.conf import settings
 4 import os
 5 
 6 
 7 # 文件上传;
 8 @require_POST
 9 def upload_file(request):
10     file = request.FILES.get('file')
11     name = file.name
12     with open(os.path.join(settings.MEDIA_ROOT,name),'wb') as fp:
13         for chunk in file.chunks():
14             fp.write(chunk)
15     url = request.build_absolute_uri(settings.MEDIA_URL + name)
16     return restful.result(data={'url':url})
View Code

使用 “get” 获取的 “file” 与前端提到 “file” 一致;

使用 build_absolute_uri 可以获取完整的url地址;

 

restful.py文件(简化每个文件代码运行结果的代码量)

 1 from django.http import JsonResponse
 2 
 3 class HttpCode(object):
 4     ok = 200            # 正常运行;
 5     paramserror = 400   # 参数错误;
 6     unauth = 401        # 没授权;
 7     methoderror = 405   # 请求方法错误;
 8     servererror = 500   # 服务器内部错误;
 9 
10 def result(code=HttpCode.ok,message='',data=None,kwargs=None):
11     json_dict = {'code':code,'message':message,'data':data}
12     if kwargs and isinstance(kwargs,dict) and kwargs.keys():
13         json_dict.update(kwargs)
14     return JsonResponse(json_dict)
15 
16 def ok():
17     return result()
18 
19 def params_error(message='',data=None):
20     return result(code=HttpCode.paramserror,message=message,data=data)
21 
22 def unauth(message='',data=None):
23     return result(code=HttpCode.unauth,message=message,data=data)
24 
25 def method_error(message='',data=None):
26     return result(code=HttpCode.methoderror,message=message,data=data)
27 
28 def server_error(message='',data=''):
29     return result(code=HttpCode.servererror,message=message,data=data)
View Code

 

 

二、上传到七牛云

 

1、登录注册七牛云:https://www.qiniu.com//

2、登录后进入右上方管理控制台后,再点击右上角的个人图标进入密钥管理,记住 AK 与 SK ,后续用到;

3、点击左侧 对象存储,新建存储空间 bucket 

 

后端:

 

1、下载 python SDKpip install qiniu

2、创建一个获取 tokenurl

 1 # 上传七牛云;
 2 @require_GET
 3 def qntokon(request):
 4     access_key = 'GjdN0XJxtrLbINSxx4ZjXXitk7Aux5h046x9viB8'
 5     secret_key = '8ULlOQezsKZIRFl_Bie09GZIJyonH50aJH6RONhu'
 6     bucket = 'qmspace'
 7     # 创建授权信息q;
 8     q = qiniu.Auth(access_key,secret_key)
 9     # 生成上传凭证,传给前端;
10     tokon = q.upload_token(bucket)
11     return restful.result(data={'tokon':tokon})
View Code

 

前端:

 

基于七牛 API 开发的前端 JavaScript SDK:https://github.com/qiniu/js-sdk(详细的官方文档讲解)

 

在HTML代码的缩略图代码下添加进度条代码,默认隐藏;

 1 <!--html代码-->
 2 <script src="https://unpkg.com/qiniu-js@2.4.0/dist/qiniu.min.js
 3 "></script>
 4 --snip--
 5 <div class="form-group">
 6     <label for="thumbnail-form">缩略图</label>
 7     --snip--
 8     </div>
 9 </div>
10 <!--上传进度条-->
11 <div class="form-group" id="progress-group" style="display: none;">
12     <div class="progress">
13       <div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="20"
14            aria-valuemin="0" aria-valuemax="100" style="width: 0">
15           0%
16       </div>
17     </div>
18 </div>
View Code

 

通过 js 代码实现与七牛云的对接及文件的上传,以及进度条的显示情况;

 1 // 上传到七牛云;
 2 News.prototype.ListenQiniuuploadFileEvent = function(){
 3     var self = this;
 4     var uploadBtn = $('#thumbnail-btn');
 5     uploadBtn.change(function () {
 6         var file = this.files[0];
 7         xfzajax.get({
 8             'url': '/cms/qntokon/',
 9             'success': function (result) {
10                 if (result['code'] ===200 ){
11                     
12                     // token: 上传验证信息,前端通过接口请求后端获得;
13                     var tokon = result['data']['tokon'];
14                     
15                     // key: 文件资源名(当前时间+文件格式);
16                     var key = (new Date()).getTime() + '.' + file.name.split('.')[-1];
17                     
18                     var putExtra = {
19                         fname: key,
20                         params: {},
21                         mimeType: ['image/png','image/jpeg','image/gif','video/x-ms-wmv','video/mp4','video/x-flv']
22                     };
23                     
24                     var config = {
25                         useCdnDomain: true,
26                         retryCount: 6,
27                         region: qiniu.region.z2
28                     };
29                     
30                     // 文件上传;
31                     var observable = qiniu.upload(file,key,tokon,putExtra,config);
32                     observable.subscribe({
33                         'next': self.handFileUploadProcess,
34                         'error': self.handFileUploadError,
35                         'complete': self.handFileUploadComplete,
36                     })
37                 }
38             }
39         })
40     })
41 };
42 
43 News.prototype.handFileUploadProcess = function(response){
44     var total = response.total;
45     var percent = total.percent;
46     var percentText = percent.toFixed() + '%';
47     var progressGroup = News.progressGroup;
48     progressGroup.show();
49     var progressBar = $('.progress-bar');
50     progressBar.css({"width":percentText});
51     progressBar.text(percentText);
52 };
53 
54 News.prototype.handFileUploadError = function(error){
55     window.messageBox.showError(error.message);
56     var progressGroup = News.progressGroup;
57     progressGroup.hide();
58     console.log(error.message);
59 };
60 
61 News.prototype.handFileUploadComplete = function(response){
62     console.log(response);
63     var progressGroup = News.progressGroup;
64     progressGroup.hide();
65     var domain = 'http://ps96zui1h.bkt.clouddn.com/';
66     var filename = response.key;
67     var url = domain + filename;
68     var thumbnailInput = $("input[name='thumbnail']");
69     thumbnailInput.val(url)
70 };
71 
72 $(function () {
73     var news = new News();
74     news.run();
75     News.progressGroup = $('#progress-group');
76 });
View Code

(官方文档很详细,不再做个人注解)

 

上传成功:

 

posted @ 2019-05-28 18:20  F·灬小人物  阅读(952)  评论(0编辑  收藏  举报