Authored by 毕凯

Merge branch 'feature/upsinglefile' into 'master'

Feature/upsinglefile



See merge request !17
1 'use strict'; 1 'use strict';
2 2
  3 +const CDNCONFIG = require('../../../config/cdn_config');
3 const _ = require('lodash'); 4 const _ = require('lodash');
4 const moment = require('moment'); 5 const moment = require('moment');
5 const Router = require('koa-router'); 6 const Router = require('koa-router');
  7 +const fs = require('fs');
  8 +const path = require('path');
6 const router = new Router(); 9 const router = new Router();
7 const FileApi = require('../../ci/file'); 10 const FileApi = require('../../ci/file');
8 -const CDNCONFIG = require('../../../config/cdn_config');  
9 -const qn = require('../../../lib/qiniu')(CDNCONFIG.feature); 11 +const qnFeature = require('../../../lib/qiniu')(CDNCONFIG.feature);
10 12
11 /** 13 /**
12 * 处理列表数据 14 * 处理列表数据
@@ -18,25 +20,18 @@ const _handleListItems = items => { @@ -18,25 +20,18 @@ const _handleListItems = items => {
18 let dateTime = _.parseInt(perFile.putTime.toString().substring(0, 13)); 20 let dateTime = _.parseInt(perFile.putTime.toString().substring(0, 13));
19 21
20 perFile.putTimeFormat = moment(dateTime).format('YYYY-MM-DD HH:mm'); 22 perFile.putTimeFormat = moment(dateTime).format('YYYY-MM-DD HH:mm');
21 - perFile.link = _.get(CDNCONFIG, 'feature.origin') + '/' + perFile.key; 23 + perFile.link = _.get(CDNCONFIG.feature, 'origin') + '/' + perFile.key;
22 }); 24 });
23 25
24 return items; 26 return items;
25 }; 27 };
26 28
27 -/**  
28 - * 校验是否是允许上传的文件包  
29 - */  
30 -const _verifyFiles = filesList => {  
31 - return true;  
32 -};  
33 -  
34 const file = { 29 const file = {
35 /** 30 /**
36 * 文件管理页面 31 * 文件管理页面
37 */ 32 */
38 page: async(ctx) => { 33 page: async(ctx) => {
39 - await ctx.render('/action/file_page', {domain: _.get(CDNCONFIG, 'feature.origin')}); 34 + await ctx.render('/action/file_page', {domain: _.get(CDNCONFIG.feature, 'origin')});
40 }, 35 },
41 36
42 /** 37 /**
@@ -68,7 +63,7 @@ const file = { @@ -68,7 +63,7 @@ const file = {
68 params.marker = ctx.query.marker; 63 params.marker = ctx.query.marker;
69 } 64 }
70 65
71 - let result = await qn.listAsync(params); 66 + let result = await qnFeature.listAsync(params);
72 let finalResult = { 67 let finalResult = {
73 items: _handleListItems(result.items) 68 items: _handleListItems(result.items)
74 }; 69 };
@@ -116,6 +111,59 @@ const file = { @@ -116,6 +111,59 @@ const file = {
116 message: '处理完毕', 111 message: '处理完毕',
117 data: {} 112 data: {}
118 }; 113 };
  114 + },
  115 +
  116 + /**
  117 + * 单文件上传页面
  118 + */
  119 + singleFile: async(ctx) => {
  120 + await ctx.render('/action/single_file_page', {domain: _.get(CDNCONFIG.main, 'origin')});
  121 + },
  122 +
  123 + /**
  124 + * 单文件上传处理
  125 + */
  126 + singleFileUpload: async(ctx, next) => {
  127 + if ('POST' !== ctx.method) return await next();
  128 +
  129 + let file = _.get(ctx, 'request.body._files.file');
  130 + let actPath = _.get(ctx, 'request.body.actPath') || 'uploads/' + moment();
  131 + let cover = _.get(ctx, 'request.body.cover') === 'yes'; // 是否覆盖
  132 + let filePath = path.join(actPath, _.get(file, 'name'));
  133 +
  134 + if (!file) {
  135 + return ctx.body = {
  136 + code: 400,
  137 + message: '没有接收到上传的文件'
  138 + };
  139 + }
  140 +
  141 + let readable = fs.createReadStream(file.path);
  142 + let config = CDNCONFIG.main;
  143 +
  144 + if (cover) {
  145 + _.assign(CDNCONFIG.main, {
  146 + key: filePath
  147 + });
  148 + }
  149 +
  150 + let qnMain = require('../../../lib/qiniu')(config);
  151 + let upResult = await qnMain.uploadAsync(readable, {key: filePath});
  152 +
  153 + if (upResult && upResult.url) {
  154 + return ctx.body = {
  155 + code: 200,
  156 + message: '上传完毕',
  157 + data: {
  158 + url: upResult.url
  159 + }
  160 + };
  161 + } else {
  162 + return ctx.body = {
  163 + code: 400,
  164 + message: '上传失败,请重试'
  165 + };
  166 + }
119 } 167 }
120 } 168 }
121 169
@@ -123,5 +171,7 @@ router.get('/page', file.page); @@ -123,5 +171,7 @@ router.get('/page', file.page);
123 router.get('/listlogs', file.listLogs); 171 router.get('/listlogs', file.listLogs);
124 router.get('/listfiles', file.listFiles); 172 router.get('/listfiles', file.listFiles);
125 router.post('/upload', file.upload); 173 router.post('/upload', file.upload);
  174 +router.get('/single-file', file.singleFile);
  175 +router.post('/single-file-upload', file.singleFileUpload);
126 176
127 module.exports = router; 177 module.exports = router;
@@ -79,6 +79,9 @@ @@ -79,6 +79,9 @@
79 if (!$('#actPath').val()) { 79 if (!$('#actPath').val()) {
80 $('.logs').prepend('<p>state: 请输入上传地址!</p>'); 80 $('.logs').prepend('<p>state: 请输入上传地址!</p>');
81 return false; 81 return false;
  82 + } else {
  83 + $(this).attr('disabled', 'disabled');
  84 + $(this).text('上传中...');
82 } 85 }
83 actPath = `/${actPath}`; 86 actPath = `/${actPath}`;
84 formData.append('file', file); 87 formData.append('file', file);
@@ -98,8 +101,12 @@ @@ -98,8 +101,12 @@
98 $('#upload-list tr[class!=title]').remove(); 101 $('#upload-list tr[class!=title]').remove();
99 page = 1; 102 page = 1;
100 getList(); 103 getList();
  104 + $('#upload').removeAttr('disabled');
  105 + $('#upload').text('上传');
101 } else if (result.code === 400) { 106 } else if (result.code === 400) {
102 $('.logs').prepend('<p>state: 请选择上传文件!</p>'); 107 $('.logs').prepend('<p>state: 请选择上传文件!</p>');
  108 + $('#upload').removeAttr('disabled');
  109 + $('#upload').text('上传');
103 } 110 }
104 } else { 111 } else {
105 $('.logs').prepend('<p>state: 请稍后再试!</p>'); 112 $('.logs').prepend('<p>state: 请稍后再试!</p>');
@@ -125,7 +132,7 @@ @@ -125,7 +132,7 @@
125 result.data.forEach(function(item){ 132 result.data.forEach(function(item){
126 $uploadList.append(` 133 $uploadList.append(`
127 <tr> 134 <tr>
128 - <td><a href="${item.link}">${item.actPath}</a></td> 135 + <td><a target="_blank" href="${item.link}">${item.actPath}</a></td>
129 <td style="text-align:center;">${item.fileName}</td> 136 <td style="text-align:center;">${item.fileName}</td>
130 <td style="text-align:center;">${item.date}</td> 137 <td style="text-align:center;">${item.date}</td>
131 <td style="text-align:center;">${item.username}</td> 138 <td style="text-align:center;">${item.username}</td>
@@ -180,7 +187,7 @@ @@ -180,7 +187,7 @@
180 $this.parents('td').find('.get-more').attr('data-marker', result.data.marker); 187 $this.parents('td').find('.get-more').attr('data-marker', result.data.marker);
181 result.data.items.forEach(function(item){ 188 result.data.items.forEach(function(item){
182 detail += `<tr class="detail-list"> 189 detail += `<tr class="detail-list">
183 - <td><a href="${item.link}">${item.key}</a></td> 190 + <td><a target="_blank" href="${item.link}">${item.key}</a></td>
184 <td style="text-align:center;">${item.fsizekb}</td> 191 <td style="text-align:center;">${item.fsizekb}</td>
185 <td style="text-align:center;">${item.mimeType}</td> 192 <td style="text-align:center;">${item.mimeType}</td>
186 <td style="text-align:center;">${item.putTimeFormat}</td> 193 <td style="text-align:center;">${item.putTimeFormat}</td>
  1 +<div class="pageheader">
  2 + <div class="media">
  3 + <div class="pageicon pull-left">
  4 + <i class="fa fa-th-list"></i>
  5 + </div>
  6 + <div class="media-body">
  7 + <ul class="breadcrumb">
  8 + <li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
  9 + <li>静态资源</li>
  10 + </ul>
  11 + <h4>单文件上传</h4>
  12 + </div>
  13 + </div>
  14 + <!-- media -->
  15 +</div>
  16 +<!-- pageheader -->
  17 +<div class="contentpanel">
  18 + <div class="form">
  19 + <div class="form-group">
  20 + <label for=""></label>
  21 + <div class="input-group">
  22 + <span class="input-group-addon">{{domain}}/</span>
  23 + <input type="text" class="form-control" id="actPath" aria-describedby="basic-addon3" placeholder="请输入文件路径,例:20171030/v2.4.8">
  24 + </div>
  25 + </div>
  26 + <div class="form-group">
  27 + <label for="file">选择文件</label>
  28 + <input type="file" class="form-control" name="file" id="fileInput">
  29 + </div>
  30 + <input type="hidden" id="cover" name="cover">
  31 + <div class="form-group">
  32 + <button class="btn btn-default" id="upLoadBtn">上传</button>
  33 + <span id="resultMsg"></span>
  34 + </div>
  35 + </div>
  36 +</div>
  37 +
  38 +<script>
  39 + $('#upLoadBtn').on('click', function(){
  40 + $('#upLoadBtn').attr('disabled', 'disabled');
  41 +
  42 + var file = $('#fileInput').prop('files')[0];
  43 + var actPath = $('#actPath').val();
  44 + var formData = new FormData();
  45 + var cover = $('#cover').val();
  46 +
  47 + formData.append('file', file);
  48 + formData.append('actPath', actPath);
  49 + formData.append('cover', cover);
  50 +
  51 + $.ajax({
  52 + type: 'POST',
  53 + url: '/files/single-file-upload',
  54 + cache: false,
  55 + data: formData,
  56 + dataType: 'json',
  57 + processData: false,
  58 + contentType: false,
  59 + complete: function() {
  60 + $('#upLoadBtn').removeAttr('disabled');
  61 + },
  62 + success: function(result) {
  63 + if (result.code === 200) {
  64 + $('#resultMsg').html(`<a target="_blank" href="${result.data.url}">${result.data.url}</a>`);
  65 + } else {
  66 + $('#resultMsg').html(result.message);
  67 + }
  68 + }
  69 + });
  70 + });
  71 +</script>
@@ -65,6 +65,7 @@ @@ -65,6 +65,7 @@
65 <ul class="children"> 65 <ul class="children">
66 <li><a href="/check/list">代码检查</a></li> 66 <li><a href="/check/list">代码检查</a></li>
67 <li><a href="/files/page">活动上传</a></li> 67 <li><a href="/files/page">活动上传</a></li>
  68 + <li><a href="/files/single-file">单文件上传</a></li>
68 </ul> 69 </ul>
69 </li> 70 </li>
70 71
@@ -3,6 +3,13 @@ @@ -3,6 +3,13 @@
3 */ 3 */
4 4
5 const CDNCONFIG = { 5 const CDNCONFIG = {
  6 + main: {
  7 + accessKey: 'cY9B5ZgON_7McTS5zV5nTeRyQ98MOcVD7W4eGVbE',
  8 + secretKey: 'RduqgmK7cAtaQvdIa1ax_zzmMsnv9ac-Ka0uF6wG',
  9 + origin: 'https://cdn.yoho.cn',
  10 + bucket: 'yohocdn'
  11 + },
  12 +
6 feature: { 13 feature: {
7 origin: 'https://feature.yoho.cn', 14 origin: 'https://feature.yoho.cn',
8 bucket: 'yohogirlzine' 15 bucket: 'yohogirlzine'
@@ -6,22 +6,21 @@ @@ -6,22 +6,21 @@
6 6
7 'use strict'; 7 'use strict';
8 8
  9 +const CDNCONFIG = require('../config/cdn_config');
9 const qn = require('qn'); 10 const qn = require('qn');
10 const Promise = require('bluebird'); 11 const Promise = require('bluebird');
11 const _ = require('lodash'); 12 const _ = require('lodash');
12 13
13 -const config = {  
14 - accessKey: 'cY9B5ZgON_7McTS5zV5nTeRyQ98MOcVD7W4eGVbE',  
15 - secretKey: 'RduqgmK7cAtaQvdIa1ax_zzmMsnv9ac-Ka0uF6wG',  
16 - origin: 'http://cdn.yoho.cn',  
17 - bucket: 'yohocdn'  
18 -};  
19 -  
20 const _default = (params) => { 14 const _default = (params) => {
21 - let finalConfig = config; 15 + let finalConfig = _.cloneDeep(CDNCONFIG.main);
22 16
23 if (params) { 17 if (params) {
24 _.assign(finalConfig, params); 18 _.assign(finalConfig, params);
  19 +
  20 + // 传 key 强制覆盖
  21 + if (params.key) {
  22 + finalConfig.bucket = params.bucket + ':' + params.key;
  23 + }
25 } 24 }
26 25
27 return Promise.promisifyAll( 26 return Promise.promisifyAll(
@@ -29,8 +28,11 @@ const _default = (params) => { @@ -29,8 +28,11 @@ const _default = (params) => {
29 ); 28 );
30 } 29 }
31 30
  31 +/**
  32 + * 兼容旧的强制覆盖逻辑,新功能开发弃用
  33 + */
32 _default.key = function(key) { 34 _default.key = function(key) {
33 - let _conifg = _.clone(config); 35 + let _conifg = _.clone(CDNCONFIG.main);
34 _conifg.bucket = _conifg.bucket + ":" + key; 36 _conifg.bucket = _conifg.bucket + ":" + key;
35 return Promise.promisifyAll(qn.create(_conifg)); 37 return Promise.promisifyAll(qn.create(_conifg));
36 }; 38 };
This diff could not be displayed because it is too large.