Browse Source

资料归集,大文件断点续传

MaiXinRong 2 years ago
parent
commit
13d172ba03

+ 4 - 3
app/controller/file_controller.js

@@ -225,13 +225,14 @@ module.exports = app => {
                 if (!filing || filing.is_deleted) throw '分类不存在,请刷新页面后重试';
 
                 let result;
+                const fileInfo = path.parse(data.fileInfo.filename);
                 switch(data.type) {
                     case 'begin':
                         const create_time = Date.parse(new Date()) / 1000;
-                        const fileInfo = path.parse(data.fileInfo.filename);
                         result = {
-                            filepath: `sp/file/${filing.spid}/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + fileInfo.ext}`
+                            filename: `sp/file/${filing.spid}/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + fileInfo.ext}`,
                         };
+                        result.filepath = ctx.app.config.fujianOssFolder + result.filename;
                         // todo 写入ossToken
                         result.oss = await ctx.helper.getOssToken(ctx.app.fujianOss);
                         break;
@@ -239,7 +240,7 @@ module.exports = app => {
                         const user = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
                         const uploadFiles = [{
                             filepath: data.filepath,
-                            filename: data.filename, fileext: data.fileext, filesize: data.fileInfo.size,
+                            filename: fileInfo.name, fileext: fileInfo.ext, filesize: data.fileInfo.filesize,
                         }];
                         result = await ctx.service.file.addFiles(filing, uploadFiles, user);
                         break;

+ 21 - 0
app/extend/helper.js

@@ -1632,5 +1632,26 @@ module.exports = {
             uniqGroup[g[0][key]] = g;
         }
         return uniqGroup.filter(x => { return !!x });
+    },
+    async getOssToken(oss) {
+        try {
+            const { STS } = require('ali-oss');
+            let sts = new STS({
+                accessKeyId: 'LTAI5tSoKgJ9Ze5VcgMQcM3m',
+                accessKeySecret: '34sj51AEUpeDzr5V6DYjrxWOatw7Zr',
+            });
+            const token = {
+                bucket: oss.options.bucket,
+                endpoint: this.ctx.app.config.fujianOssPath,
+                cname: true,
+            };
+            const result = await sts.assumeRole('acs:ram::31196920:role/oss-readonly', '', '3000', this.ctx.app.uuid.v4());
+            token.accessKeyId = result.credentials.AccessKeyId;
+            token.accessKeySecret = result.credentials.AccessKeySecret;
+            token.stsToken = result.credentials.SecurityToken;
+            return token;
+        } catch (err) {
+            console.log(err);
+        }
     }
 };

+ 28 - 0
app/public/js/file_detail.js

@@ -232,6 +232,27 @@ $(document).ready(function() {
                 if (callback) callback();
             });
         }
+        uploadBigFile(file, callback) {
+            if (file.size > 500 * 1024 * 1024) {
+                toastr.error('上传文件大小超过500MB。');
+                return false;
+            }
+            const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
+            if (whiteList.indexOf(fileext) === -1) {
+                toastr.error('仅支持office文档、图片、压缩包格式,请勿上传' + fileext + '格式文件。');
+                return false;
+            }
+            AliOss.uploadBigFile(file, 'file/upload/big', { filing_id: filingObj.curFiling.id },
+                { progressObj: $('#upload-big-file-progress'), resumeObj: $('#add-big-file-resume'), stopObj: $('#add-big-file-stop') },
+                function(result) {
+                    filingObj.curFiling.source_node.files.unshift(...result.files);
+                    filingObj.updateFilingFileCount(filingObj.curFiling, result.filing.file_count);
+                    filingObj.refreshFilesTable();
+                    filingObj.refreshPages();
+                    if (callback) callback();
+            });
+
+        }
         delFiles(files, callback) {
             postData('file/del', { del: files }, async function(data) {
                 for (const id of data.del) {
@@ -515,6 +536,13 @@ $(document).ready(function() {
             $('#add-file').modal('hide');
         });
     });
+    $('#add-big-file-ok').click(() => {
+        const input = $('#upload-big-file');
+        filingObj.uploadBigFile(input[0].files[0], function () {
+            $('#upload-big-file').val('');
+            $('#add-big-file').modal('hide');
+        });
+    });
     $('body').on('mouseenter', ".table-file", function(){
         $(this).children(".btn-group-table").css("display","block");
     });

+ 118 - 1
app/public/js/shares/ali_oss.js

@@ -2,6 +2,7 @@ const AliOss = (function (){
     const setting = {
         hint: true
     };
+    let client, uploadInfo;
 
     const downloadFile = function (url, filename) {
         axios.get(url, {responseType: 'blob' }).then(res => {
@@ -54,5 +55,121 @@ const AliOss = (function (){
         setting.hint = data.hint || setting.hint;
     };
 
-    return { downloadFile, downloadFileSync, zipFiles, setSetting }
+    const stopUpload = async function() {
+        if (!client) toastr.warning('当前没有上传中的大文件');
+
+        client.cancel();
+        uploadInfo.isStop = true;
+        if (uploadInfo.stopObj) uploadInfo.stopObj.attr('disabled', 'disabled');
+    };
+
+    const resumeUpload = async function() {
+        if (!client || !uploadInfo.abortCheckpoint) return;
+
+        if (uploadInfo.stopObj) uploadInfo.stopObj.removeAttr('disabled');
+        if (uploadInfo.resumeObj) uploadInfo.resumeObj.attr('disabled', 'disabled');
+        try {
+            const result = await client.multipartUpload(uploadInfo.filepath, uploadInfo.file, {
+                checkpoint: uploadInfo.abortCheckpoint,
+                progress: (p, cpt, res) => {
+                    uploadInfo.abortCheckpoint = cpt;
+                    // 刷新进度条
+                    if (uploadInfo.progressObj) {
+                        uploadInfo.progressObj.attr('aria-valuenow', p * 100);
+                        uploadInfo.progressObj.width((p * 100) + '%');
+                    }
+                }
+            });
+
+            return await endUploadBigFile();
+        } catch (e) {
+            if (uploadInfo.resumeObj) uploadInfo.resumeObj.removeAttr('disabled');
+        }
+    };
+
+    const resetRelaObj = function() {
+        if (uploadInfo.progressObj) {
+            uploadInfo.progressObj.attr('aria-valuenow', 0);
+            uploadInfo.progressObj.width('0%');
+        }
+        if (uploadInfo.stopObj) {
+            uploadInfo.stopObj.show();
+            uploadInfo.stopObj.removeAttr('disabled');
+            uploadInfo.stopObj.bind('click', function() {
+                stopUpload();
+            });
+        }
+        if (uploadInfo.resumeObj) {
+            uploadInfo.resumeObj.show();
+            uploadInfo.resumeObj.attr('disabled', 'disabled');
+            uploadInfo.resumeObj.bind('click', function() {
+                resumeUpload();
+            });
+        }
+    };
+
+    const clearRelaObj = function() {
+        if (uploadInfo.stopObj) {
+            uploadInfo.stopObj.attr('disabled', 'disabled');
+            uploadInfo.stopObj.unbind('click');
+        }
+        if (uploadInfo.resumeObj) {
+            uploadInfo.resumeObj.attr('disabled', 'disabled');
+            uploadInfo.resumeObj.unbind('click');
+        }
+    };
+
+    const beginUploadBigFile = async function () {
+        const beingData = JSON.parse(JSON.stringify(uploadInfo.defaultData));
+        beingData.type = 'begin';
+        beingData.fileInfo = uploadInfo.fileInfo;
+        const info = await postDataAsync(uploadInfo.url, beingData, false);
+        uploadInfo.filepath = info.filepath;
+        uploadInfo.filename = info.filename;
+        if (!info.oss) throw '授权信息错误';
+        uploadInfo.oss = info.oss;
+    };
+    const endUploadBigFile = async function() {
+        client = null;
+        const endData = JSON.parse(JSON.stringify(uploadInfo.defaultData));
+        endData.type = 'end';
+        endData.filepath = uploadInfo.filename;
+        endData.fileInfo = uploadInfo.fileInfo;
+        clearRelaObj();
+        const uploadRes = await postDataAsync(uploadInfo.url, endData, false);
+        if (uploadInfo.callback) {
+            uploadInfo.callback(uploadRes);
+        }
+        uploadInfo = {};
+        return uploadRes;
+    };
+
+    const uploadBigFile = async function (file, url, data, relaObj, callback) {
+        uploadInfo = { url, file, ...relaObj, callback };
+        uploadInfo.fileInfo = { filename: file.name, filesize: file.size, type: file.type, lastModified: file.lastModified };
+        uploadInfo.defaultData = data;
+        await beginUploadBigFile();
+
+        try {
+            client = new OSS(uploadInfo.oss);
+            resetRelaObj();
+            const result = await client.multipartUpload(uploadInfo.filepath, uploadInfo.file, {
+                progress: (p, cpt, res) => {
+                    uploadInfo.abortCheckpoint = cpt;
+                    // 刷新进度条
+                    if (uploadInfo.progressObj) {
+                        uploadInfo.progressObj.attr('aria-valuenow', p * 100);
+                        uploadInfo.progressObj.width((p * 100) + '%');
+                    }
+                }
+            });
+
+            return await endUploadBigFile();
+        } catch (err) {
+            if (uploadInfo.resumeObj) uploadInfo.resumeObj.removeAttr('disabled');
+            if (uploadInfo.stopObj) uploadInfo.stopObj.attr('disabled', 'disabled');
+        }
+    };
+
+    return { downloadFile, downloadFileSync, zipFiles, setSetting, uploadBigFile, resumeUpload }
 })();

File diff suppressed because it is too large
+ 27421 - 0
app/public/js/shares/aliyun-oss-sdk.min.js


+ 2 - 2
app/public/js/tender_list_info.js

@@ -42,7 +42,7 @@ const tenderListSpec = (function(){
         // 当前流程
         html.push('<td style="width: 230px">');
         if (!node.cid && node.cur_flow) {
-            const curUser = node.cur_flow instanceof Array
+            const curUser = node.cur_flow instanceof Array && node.cur_flow[0].audit_type !== auditType.key.common
                 ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
                 : node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : '');
             if (node.stage_status !== undefined) {
@@ -58,7 +58,7 @@ const tenderListSpec = (function(){
                 html.push(curUser);
                 html.push((node.lastStage && node.lastStage.status === auditConst.stage.status.uncheck) || (!node.lastStage && node.ledger_status === auditConst.ledger.status.uncheck ) ? ' ':
                     '</a> ');
-                if (node.cur_flow instanceof Array) {
+                if (node.cur_flow instanceof Array && node.cur_flow[0].audit_type !== auditType.key.common) {
                     html.push('<span class="' + node.cur_flow[0].status_class +'">' + node.cur_flow[0].status + '</span>');
                 } else {
                     html.push('<span class="' + node.cur_flow.status_class +'">' + node.cur_flow.status + '</span>');

+ 1 - 1
app/public/js/tender_list_progress.js

@@ -53,7 +53,7 @@ const tenderListSpec = (function(){
         // 当前流程
         html.push('<td style="width: 8%">');
         if (!node.cid && node.cur_flow) {
-            const curUser = node.cur_flow instanceof Array
+            const curUser = node.cur_flow instanceof Array && node.cur_flow[0].audit_type && node.cur_flow[0].audit_type !== auditType.key.common
                 ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
                 : node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : '');
             if (node.stage_status !== undefined) {

+ 1 - 1
app/service/stage_audit.js

@@ -645,7 +645,7 @@ module.exports = app => {
                     cache_time_r: this.ctx.stage.cache_time_l,
                 });
                 await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, checkData.checkType,
-                    [{ aid: this.ctx.stage.user_id, order: 0, status: auditConst.status.uncheck }], flowAudits, ledgerTp, stageTp);
+                    [{ aid: this.ctx.stage.user_id, audit_order: 0, audit_type: auditType.key.common, status: auditConst.status.uncheck }], flowAudits, ledgerTp, stageTp);
                 // 拷贝新一次审核流程列表
                 await transaction.insert(this.tableName, newAuditors);
                 // 计算该审批人最终数据

+ 2 - 2
app/service/tender_cache.js

@@ -381,7 +381,7 @@ module.exports = app => {
                     lastStage.curOrder = 0;
                     await this._calcTp(lastStage, tp);
                     data.stage_flow_cur_uid = lastStage.user_id;
-                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: 0, status: lastStage.status });
+                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: lastStage.order, audit_order: 0, audit_type: auditConst.auditType.key.common, status: lastStage.status });
                     data.stage_flow_cur_info = JSON.stringify(curInfo);
                     data.stage_flow_cur_tp = JSON.stringify(tp);
                 } else if (lastStage.status === auditConst.stage.status.checked) {
@@ -400,7 +400,7 @@ module.exports = app => {
                     lastStage.curOrder = 0;
                     await this._calcTp(lastStage, tp);
                     data.stage_flow_cur_uid = lastStage.user_id;
-                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: 0, status: auditConst.stage.status.uncheck });
+                    const curInfo = await this.ctx.service.projectAccount.getAccountCacheData(lastStage.user_id, { order: lastStage.order, audit_order: 0, audit_type: auditConst.auditType.key.common, status: auditConst.stage.status.uncheck });
                     data.stage_flow_cur_info = JSON.stringify(curInfo);
                     data.stage_flow_cur_tp = JSON.stringify(tp);
                     const preAuditors = await this.ctx.service.stageAudit.getLastestAuditors(lastStage.id, lastStage.times - 1, lastStage.status);

+ 3 - 0
app/view/file/file.ejs

@@ -35,6 +35,9 @@
                     <div class="d-flex flex-row">
                         <% if (canUpload) { %>
                         <div class="py-2 pr-2"><a href="#add-file" data-toggle="modal" data-target="#add-file">上传文件</a></div>
+                        <% if (ctx.app.config.is_debug) { %>
+                        <div class="py-2 pr-2"><a href="#add-big-file" data-toggle="modal" data-target="#add-big-file">大文件上传</a></div>
+                        <% } %>
                         <div class="p-2" id="rela-file-btn"><a href="#rela-file" data-toggle="modal" data-target="#rela-file">导入文件</a></div>
                         <div class="p-2"><a href="javascript: void(0)" id="batch-del-file-btn">批量删除</a></div>
                         <% } %>

+ 24 - 0
app/view/file/file_modal.ejs

@@ -82,6 +82,30 @@
         </div>
     </div>
 </div>
+<div class="modal fade" id="add-big-file" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">上传附件</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label for="formGroupExampleInput">文件大小限制:500MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="" id="upload-big-file">
+                </div>
+                <div class="form-group progress">
+                    <div id="upload-big-file-progress" class="progress-bar" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-primary btn-sm" id="add-big-file-stop" style="display: none">暂停</button>
+                <button type="button" class="btn btn-primary btn-sm" id="add-big-file-resume" style="display: none">重新上传</button>
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="add-big-file-ok">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
 <div class="modal fade" id="batch-del-file" data-backdrop="static" style="display: none;" aria-hidden="true">
     <div class="modal-dialog" role="document">
         <div class="modal-content">

+ 12 - 12
app/view/stage/audit_modal.ejs

@@ -119,10 +119,10 @@
                                             <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
                                             <% } %>
                                         </span>
-                                        <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
                                         <% if (item[0].audit_type !== auditType.key.common) { %>
-                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 ml-auto"><small><%- auditType.info[item[0].audit_type].short %></small></span>
                                         <% } %>
+                                        <span class="badge badge-light badge-pill"><small>终审</small></span>
                                     </li>
                                     <% } else {%>
                                     <li class="list-group-item d-flex justify-content-between align-items-center">
@@ -132,10 +132,10 @@
                                             <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
                                             <% } %>
                                         </span>
-                                        <span class="badge badge-light badge-pill ml-auto"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
                                         <% if (item[0].audit_type !== auditType.key.common) { %>
-                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <span class="badge badge-pill badge-<%- auditType.info[item[0].audit_type].class %> p-1 ml-auto"><small><%- auditType.info[item[0].audit_type].short %></small></span>
                                         <% } %>
+                                        <span class="badge badge-light badge-pill"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
                                     </li>
                                     <% } %>
                                     <% }) %>
@@ -290,10 +290,10 @@
                                             <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
                                             <% } %>
                                         </span>
-                                        <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
                                         <% if (item[0].audit_type !== auditType.key.common) { %>
-                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 ml-auto"><small><%- auditType.info[item[0].audit_type].short %></small></span>
                                         <% } %>
+                                        <span class="badge badge-light badge-pill"><small>终审</small></span>
                                     </li>
                                     <% } else {%>
                                     <li class="list-group-item d-flex justify-content-between align-items-center">
@@ -303,10 +303,10 @@
                                             <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
                                             <% } %>
                                         </span>
-                                        <span class="badge badge-light badge-pill ml-auto"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
                                         <% if (item[0].audit_type !== auditType.key.common) { %>
-                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 ml-auto"><small><%- auditType.info[item[0].audit_type].short %></small></span>
                                         <% } %>
+                                        <span class="badge badge-light badge-pill"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
                                     </li>
                                     <% } %>
                                     <% }) %>
@@ -464,10 +464,10 @@
                                             <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
                                             <% } %>
                                         </span>
-                                        <span class="badge badge-light badge-pill ml-auto"><small>终审</small></span>
                                         <% if (item[0].audit_type !== auditType.key.common) { %>
-                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 ml-auto"><small><%- auditType.info[item[0].audit_type].short %></small></span>
                                         <% } %>
+                                        <span class="badge badge-light badge-pill"><small>终审</small></span>
                                     </li>
                                     <% } else {%>
                                     <li class="list-group-item d-flex justify-content-between align-items-center">
@@ -477,10 +477,10 @@
                                             <small class="d-inline-block text-dark mx-1" title="<%- u.role %>" data-auditorId="<%- u.aid %>"><%- u.name %></small>
                                             <% } %>
                                         </span>
-                                        <span class="badge badge-light badge-pill ml-auto"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
                                         <% if (item[0].audit_type !== auditType.key.common) { %>
-                                        <div class="li-subscript"><span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 badge-bg-small"><small><%- auditType.info[item[0].audit_type].short %></small></span></div>
+                                        <span class="badge badge-pill badge-<%-  auditType.info[item[0].audit_type].class %> p-1 ml-auto"><small><%- auditType.info[item[0].audit_type].short %></small></span>
                                         <% } %>
+                                        <span class="badge badge-light badge-pill"><small><%= ctx.helper.transFormToChinese(idx) %>审</small></span>
                                     </li>
                                     <% } %>
                                     <% }) %>

+ 1 - 0
config/web.js

@@ -1065,6 +1065,7 @@ const JsFiles = {
                 files: [
                     '/public/js/axios/axios.min.js', '/public/js/file-saver/FileSaver.js', '/public/js/js-xlsx/jszip.min.js',
                     '/public/js/moment/moment.min.js', '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js',
+                    '/public/js/shares/aliyun-oss-sdk.min.js',
                 ],
                 mergeFiles: [
                     '/public/js/shares/ali_oss.js',