免Flash文件上传

无需调用Flash,从课程平台上传附件,不必为了传作业多装一个浏览器!

当前为 2021-09-22 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              免Flash文件上传
// @name:en           Upload without Flash
// @namespace         https://greasyfork.org/zh-CN/users/605474
// @version           1.2.2
// @description       无需调用Flash,从课程平台上传附件,不必为了传作业多装一个浏览器!
// @description:en    No need to call Flash, upload accessories from the course platform, do not have to download another browsers!
// @author            Ziu
// @match             *://cc.bjtu.edu.cn:81/*
// @match             *://cc.bjtu.edu.cn:81/meol/common/hw/student/write.jsp?hwtid=*
// @icon              https://gitee.com/ziuc/utool-filebed/raw/master/20210514-231824-0795.png
// @require           https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
// @require           https://cdn.bootcdn.net/ajax/libs/jquery/1.7.2/jquery.min.js
// @require           https://cdn.bootcdn.net/ajax/libs/clipboard.js/2.0.8/clipboard.min.js
// @grant             none
// @license           MIT
// ==/UserScript==

(function() {
    'use strict';
    // 上传回调函数全局变量
    let index = 0;
    let isAbort = 0;
    let oloaded = 0;
    let ot = 0;

    // 预置DOM
    let frontTag = "<li><a id='add_button' title='上传作业' onclick=location='http://cc.bjtu.edu.cn:81/meol/common/hw/student/hwtask.jsp?tagbug=client&strStyle=new06'><span>上传作业</span></a></li>";
    let uploadBox = '<tr><td><div id="outerDiv" style="font-size: 14px; width: 550px; margin:0 auto; margin-top: 10px;"><div id="inputDiv" title="点击选择文件上传" style="height: 200px; width: 560px; cursor:pointer; border: 3px dashed; border-radius:10px; border-color: #82b900; background: #c6f062;"><div id="inputArea"><div id="textShow" style="margin-top: 80px; font-size: 20px; color: #5c6b77; text-align: center;"><p id="p1">📁文件上传📝</p><p id="p2" style="font-size: 15px">(支持拖拽上传文件)</p></div><input id="currentFile" type="file" multiple="multiple" style="display:none;" </input></div></div><div id="filenames" style="margin-top: 10px; margin-bottom: 10px; background-color: #e7e8e0;"></div><div id="buttonDiv" style="margin-top: 10px; margin-bottom: 10px; text-align: center"></div></div></td></tr>'
    let uploadBtn = '<a id="uploadTrigger" title="上传" style="cursor:pointer; padding-top: 2.5px; padding-bottom: 2.5px; padding-right: 5px; padding-left: 5px; background-color: #c6f062; border: 2px solid;">上传</a>&nbsp&nbsp';
    let emptyBtn = '<a id="emptyTrigger" title="清空" style="cursor:pointer; padding-top: 2.5px; padding-bottom: 2.5px; padding-right: 5px; padding-left: 5px; background-color: #c6f062; border: 2px solid;">清空</a>';

    // 外部DOM操作
    $('#tmenu').append(frontTag);
    $('.infotable>tbody>tr:contains("请输入你的答案")').after(uploadBox);

    // 信息显示部分
    let upldIconList = ['🕒','⚡','✅','🕒','⌛','🔒','✅']; // 所有icon统一管理
    let infoIconList = ['🔄','⚠','🚫','🕒','⌛','🔒','✅'];
    let fileTypeIconList = ['📄','⛺','📚','🎬','📝','📜'];

    // 动效
    $('#inputDiv').mouseenter(function (){$('#inputDiv').css('background-color','#87bd04');$('#textShow').css('color','#e7e8e0')});
    $('#inputDiv').mouseleave(function (){$('#inputDiv').css('background-color','#c6f062');$('#textShow').css('color','#5c6b77')});
    $('tr[class=even]').mouseenter(function (){$(this).attr('class','even over');});

    // 文件选择前事件监听
    $('#inputDiv').click(function (){$('#currentFile').trigger('click');});
    $('#currentFile').change(fileChangedByInput);

    function fileChangedByInput(){
        let filelist = $('#currentFile')[0].files;
        // 缓存区展示
        $('#inputDiv').hide(); // 隐藏上传框
        $('#filenames, #buttonDiv').empty(); // 先清空 再插入
        let sizeType = getSizeType(filelist);
        let nameType = getNameType(filelist);
        let fileType = getFileType(filelist);
        for(let i=0;i<filelist.length;i++){
            let item = {};
            item.file = filelist[i];
            item.sizeType = sizeType[i];
            item.initName = item.file.name;
            item.showName = nameType[i];
            item.fileTypeIcon = fileType[i].icon;
            item.fileTypeInfo = fileType[i].info;
            let fileObject = '<div class="fileObjects" id="fileTh'+i+'"style="cursor:default; margin-top: 10px; background-color: #e7e8e0;background-image: url(http://cc.bjtu.edu.cn:81/meol/styles/newstyle/course/new06/body_bg.jpg); background-repeat:no-repeat; background-size: 0%;"><a class="fileindex" id="fileindex'+i+'" title="'+item.fileTypeInfo+'" type="'+item.file.type+'" style="margin-left: 5px;">'+item.fileTypeIcon+'</a>&nbsp&nbsp|&nbsp&nbsp<a class="filename" id="filenameTH'+i+'" title="'+item.initName+'" data-clipboard-text="">'+item.showName+'<a class="fileSize">  ('+item.sizeType.size+item.sizeType.type+')</a>'+'</a><a class="uploadSpeed" id="speedTh'+i+'" title="上传速度"></a><a class="timeRemain" id="timeRemainTh'+i+'"></a></div>';
            $('#filenames').append(fileObject);
        }

        // 文件选择后外部DOM插入
        $('#buttonDiv').append(uploadBtn);
        $('#buttonDiv').append(emptyBtn);

        // 文件选择后事件监听
        $('#emptyTrigger').click(function (){$('#currentFile').val('');$('#filenames, #buttonDiv').empty();$('#inputDiv').show();isAbort=1;});
        $('#uploadTrigger').click(function (){
            // 处理文件缓存区
            $('#uploadTrigger').hide();
            $('#uploadTrigger').css('cursor','not-allowed');
            isAbort = 0; // 按上传之前按过清空,将isAbort置回0
            sendFileMsg();
            function sendFileMsg(){
                if(index >= filelist.length){
                    // 递归结束
                    $('#currentFile').val(''); // <input>文件清空
                    $('#emptyTrigger').css('cursor','pointer');
                    index = 0; // 递归条件置零
                    return // 结束递归
                }

                let formData = new FormData();
                formData.append('Filename', filelist[index].name);
                formData.append('Filedata', filelist[index]);

                let xReq = new XMLHttpRequest();
                xReq.open('POST','http://cc.bjtu.edu.cn:81/meol/servlet/SerUpload');
                xReq.addEventListener("load", onSuccess);
                xReq.addEventListener("error", onError);
                xReq.upload.onloadstart = onStart;
                xReq.upload.onprogress = onProgress;
                xReq.upload.onabort = onAbort;
                xReq.send(formData);

                function onStart(){
                    ot = new Date().getTime(); // 设置上传开始时间,用以计算时间速度
                    oloaded = 0; // 设置上传开始时,已上传的文件大小为0
                }

                function onProgress(evt){
                    if(isAbort==1){
                        xReq.abort(); // 上传过程中随时检查,终止请求
                    }
                    // 进度计算
                    let percentage;
                    percentage = (evt.loaded * 100 / evt.total).toFixed(0);

                    // 速度计算
                    let nt = new Date().getTime(); // 获取当前时间
                    let perTime = (nt-ot)/1000; // 计算出上次调用该方法时到现在的时间差,单位为s
                    ot = new Date().getTime(); //重新赋值,用以下次计算
                    let perLoad = evt.loaded - oloaded; // 计算该分段上传的文件大小,单位b
                    oloaded = evt.loaded; // 重新赋值,用以下次计算
                    let speed = perLoad/perTime; // 单位 B/s
                    let bspeed = speed;
                    let Sunits = 'B/s';
                    if(speed/1024>1){
                        speed = speed/1024;
                        Sunits = 'KB/s'
                    }
                    if(speed/1024>1){
                        speed = speed/1024;
                        Sunits = 'MB/s';
                    }
                    speed = speed.toFixed(1);

                    // 时间计算 文件>100MB触发
                    if(evt.total>104857600){
                        let restTime = ((evt.total-evt.loaded)/bspeed).toFixed(0);
                        // 更新文件缓存区
                        $('#timeRemainTh'+index).html('&nbsp | &nbsp'+upldIconList[0]+restTime+'秒');
                    }

                    // 更新文件缓存区
                    $('#fileTh'+index).css('background-size',percentage+'%');
                    $('#speedTh'+index).html('&nbsp | &nbsp'+upldIconList[1]+speed+Sunits);
                }

                function onAbort(){
                    isAbort = 0; // 终止条件置零
                    index = 0; // 请求终止后index也必须归零,因为中止未完成递归,若中止文件index>0则第二次上传请求无法发起
                }

                function onSuccess(){
                    // 插入编辑器操作
                    let myiframe = document.getElementsByTagName('iframe')[1].contentDocument;
                    let textArea = myiframe.getElementsByClassName('cke_show_borders');
                    let constructor = '<p><a data-cke-saved-href="/meol/'+this.responseText+'" href="/meol/'+this.responseText+'">'+filelist[index].name+'</a></p>';
                    $(textArea).append(constructor);

                    // 处理缓存区操作
                    let clipboard = new ClipboardJS('#filenameTH'+index); // 要用id做索引 否则会重复调用.on 但在清空后重新上传还是会重复调用T.T
                    let fileURL = 'http://cc.bjtu.edu.cn:81/meol/'+this.responseText;

                    $('#filenameTH'+index).attr("data-clipboard-text",fileURL);
                    $('#filenameTH'+index).css('cursor','pointer');
                    $('#speedTh'+index).html('&nbsp | &nbsp'+upldIconList[2]);
                    $('#speedTh'+index).attr('title','[已上传]');
                    $('#timeRemainTh'+index).remove();
                    $('#fileTh'+index).css('background','#c6f062');

                    clipboard.on('success', function(evt) {
                        alert('【'+$(evt.trigger).text()+'】文件链接已复制到剪切板')
                        evt.clearSelection();
                    });

                    // 递归操作
                    index++;
                    sendFileMsg();
                }
                function onError(){
                    alert('上传失败');
                }
            }
        })
    }

    function getSizeType(filelist){
        let RtnsizeType = [];
        for(let item of filelist){
            let sizeTypeTMP = {};
            if(item.size<1024){
                sizeTypeTMP.size = (item.size).toFixed(2);
                sizeTypeTMP.type = 'B';
            }
            else if(item.size>1024&&item.size<1048576){
                sizeTypeTMP.size = (item.size/1024).toFixed(2);
                sizeTypeTMP.type = 'KB';
            }
            else if(item.size>1048576&&item.size<1073741824){
                sizeTypeTMP.size = (item.size/1048576).toFixed(2);
                sizeTypeTMP.type = 'MB';
            }
            else if(item.size>1073741824){
                alert('你选择了大于1GB的文件,服务器存储资源有限,建议选择其他传输方式。');
                sizeTypeTMP.size = (item.size/1073741824).toFixed(2);
                sizeTypeTMP.type = 'GB';
            }
            RtnsizeType.push(sizeTypeTMP);
        }
        return RtnsizeType;
    }

    function getNameType(filelist){
        // 文件名显示长度限制
        let nameType = [];
        let limit;

        for(let item of filelist){
            let splitName = item.name.split('.'); // 预防文件名中含.的情况
            let newName = '';
            for(let i=0;i<splitName.length-1;i++){
                newName = newName + splitName[i];
            }
            limit = isChina(newName); // 即时生成limit限制
            if(newName.length>limit){
                // 有后缀超长文件
                newName = newName.slice(0,limit) + '... .'+ splitName.pop();
            }
            else if(newName.length==0){
                // 无后缀文件
                newName = splitName[0];
            }
            else {
                // 默认去掉了后缀 把后缀加回来
                newName = newName +'.'+ splitName.pop();
            }
            nameType.push(newName);
        }

        function isChina(str){
            if (escape(str).indexOf("%u")<0){
                return 25; // 不包含中文
            } else {
                return 17; // 包含中文
            }
        }
        return nameType;
    }

    function getFileType(filelist){
        // 文件图标类型
        let fileType = [];
        for(let item of filelist){
            let fileTypeTMP = {};
            if(item.type.indexOf('image')!=-1){
                fileTypeTMP.icon = fileTypeIconList[1];
                fileTypeTMP.info = '[图像]';
            }
            else if(item.type.indexOf('zip')!=-1){
                fileTypeTMP.icon = fileTypeIconList[2];
                fileTypeTMP.info = '[压缩文件]';
            }
            else if(item.type.indexOf('video')!=-1){
                fileTypeTMP.icon = fileTypeIconList[3];
                fileTypeTMP.info = '[视频]';
            }
            else if(item.type.indexOf('officedocument')!=-1){
                fileTypeTMP.icon = fileTypeIconList[4];
                fileTypeTMP.info = '[文档]';
            }
            else if(item.type.indexOf('pdf')!=-1){
                fileTypeTMP.icon = fileTypeIconList[5];
                fileTypeTMP.info = '[文档]';
            }
            else {
                fileTypeTMP.icon = fileTypeIconList[0];
                fileTypeTMP.info = '[文件]';
            }
            fileType.push(fileTypeTMP);
        }
        return fileType;
    }

    function judgeOnceInPage(){
        if($("script:contains('该作业不允许重复提交,确定提交作业吗?')").length!=0){
            let newspan = '<span>(只能提交一次)</span>';
            $('span:contains("查看作业任务")').after(newspan);
            alert("注意,此作业只能提交一次,请检查无误后再提交!");
            return 1;
        }
        else return 0;
    }

    function getInfoList(){
        let initInfoList = $('.infolist');
        let initSubmits = $('.enter');
        let infoList = [];
        for(let i=0;i<initInfoList.length;i++){
            let infoTMP = {};
            infoTMP.id = initInfoList[i].getAttribute('href').split('=')[1];
            infoTMP.name = initInfoList[i].innerText;
            for(let j=0;j<initSubmits.length;j++){
                infoTMP.able = (initSubmits[j].getAttribute('href').split('=')[1]==infoTMP.id);
                if(infoTMP.able) break;
            }
            infoList.push(infoTMP);
            appendOnceInfo(infoTMP.id);
            appendSubmitInfo(infoTMP.id);

            function appendOnceInfo(hwtid){
                if(!infoTMP.able){
                    // 过期作业,不发请求
                    return
                }
                // 检查是否只能提交一次
                let url = 'http://cc.bjtu.edu.cn:81/meol/common/hw/student/write.jsp?hwtid='+hwtid;
                let data = 'hwtid='+hwtid;
                let xReq = new XMLHttpRequest();
                xReq.open('GET',url,false); // 注意,此处请求应当为同步请求 否则调用this拿到的数据是undefined
                xReq.addEventListener("load", onSuccess);
                xReq.addEventListener("error", onError);
                xReq.send(data);
                function onSuccess(){
                    let myResponse = this.responseText;
                    if(myResponse.indexOf('该作业不允许重复提交,确定提交作业吗?')!=-1){
                        let attentionIcon = '<a class="attentionIcon" title="此作业只允许提交一次" style="cursor:default;">'+infoIconList[5]+'</a>'
                        $(".infolist[href='hwtask.view.jsp?hwtid="+infoTMP.id+"']").before(attentionIcon);
                    }
                    else {
                        let attentionIcon = '<a class="attentionIcon" title="此作业允许重复提交" style="cursor:default;">'+infoIconList[3]+'</a>'
                        $(".infolist[href='hwtask.view.jsp?hwtid="+infoTMP.id+"']").before(attentionIcon);
                    }
                }
                function onError(){
                    alert('请求失败');
                }
            }

            function appendSubmitInfo(hwtid){
                if(!infoTMP.able){
                    // 过期作业,不发请求
                    return
                }
                // 检查是否已经提交
                let url = 'http://cc.bjtu.edu.cn:81/meol/common/hw/student/taskanswer.jsp?hwtid='+hwtid;
                let data = 'hwtid='+hwtid;
                let xReq = new XMLHttpRequest();
                xReq.responseType = "document";
                xReq.open('GET',url,true); // 注意,此处请求应当为异步请求 用以接收text/html数据
                xReq.addEventListener("load", onSuccess);
                xReq.addEventListener("error", onError);
                xReq.send(data);
                function onSuccess(){
                    let myResponse = this.responseXML;
                    let iptArea = $(myResponse).find("tr:contains('回答的内容')+tr>td>input");
                    if(iptArea.length<=0){
                        let submitIcon = '<a class="submitIcon" title="作业未提交" style="cursor:default;">'+infoIconList[4]+'</a>'
                        $(".infolist[href='hwtask.view.jsp?hwtid="+infoTMP.id+"']").before(submitIcon);
                    }
                    else{
                        let submitIcon = '<a class="submitIcon" title="作业已提交" style="cursor:default;">'+infoIconList[6]+'</a>'
                        $(".infolist[href='hwtask.view.jsp?hwtid="+infoTMP.id+"']").before(submitIcon);
                    }
                }
                function onError(){
                    alert('请求失败');
                }
            }
        }
        return infoList;
    }

    // 页面加载完毕,执行拖拽上传功能
    window.onload = function () {
        if($("span:contains('查看作业任务')").length==0){
            // 判断是否在作业页面
            return
        }
        let oBox = document.getElementById('inputDiv');
        let timer = null;
        document.ondragover = function(){
            clearTimeout(timer);
            timer = setTimeout(function(){
                // 文件放下后执行操作
                $('#p1').html('📁文件上传📝');
                $('#inputDiv').css('background-color','#c6f062');$('#textShow').css('color','#5c6b77');
            },200);
        };
        // 进入子集的时候 会触发ondragover 频繁触发 不给ondrop机会
        oBox.ondragover = function(){
            return false;
        };
        oBox.ondragleave = function(){
            $('#p1').html('📌请将文件拖拽到此区域');
            $('#inputDiv').css('background-color','#87bd04');$('#textShow').css('color','#e7e8e0');
        };
        oBox.ondrop = function(ev){
            $('#currentFile')[0].files = ev.dataTransfer.files;
            fileChangedByInput();
            return false;
        };
    };

    // 列表信息增强与单词提交判断函数触发
    judgeOnceInPage();
    getInfoList();

})();