免Flash文件上传

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

目前为 2021-09-26 提交的版本。查看 最新版本

// ==UserScript==
// @name              免Flash文件上传
// @name:en           Upload without Flash
// @namespace         https://gf.qytechs.cn/zh-CN/users/605474
// @version           1.3
// @description       无需调用Flash,从课程平台上传附件,不必为了传作业多装一个浏览器!
// @description:en    No need to call Flash, upload accessories from the course platform, do not have to make multiple browsers for the homework!
// @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 onUpload = 0;
    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: 590px; 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 processImg = 'http://cc.bjtu.edu.cn:81/meol/styles/newstyle/course/new06/body_bg.jpg';
    let upldIconList = ['🕒','⚡','✅','🕒','⌛','🔒','✅']; // 所有icon统一管理
    let infoIconList = ['🔄','⚠','🚫','🕒','⌛','🔒','✅'];
    let fileTypeIconList = ['📄','⛺','📚','🎬','📝','📜'];

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

    function appendObjects(filelist){
        // 缓存区更新前DOM操作
        $('#filenames, #buttonDiv').empty(); // 清空缓存区
        $('#inputDiv').hide(); // 隐藏上传框
        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('+processImg+'); background-repeat:no-repeat; background-size: 0%;"><a class="fileindex" id="fileindex'+i+'" title="'+item.fileTypeInfo+'" type="'+item.file.type+'" style="cursor:pointer; 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);
        // 动效 (所有Object都已插入完毕)
        $('.fileObjects').mouseenter(function (){
            // 1.变背景色 2.文字加粗 3.展示全名
            let prevBgc = $(this).css('background-color');
            let prevName = $(this).find('.filename').html();
            $(this).css('background-color','#a7cd43');
            $(this).find('.filename').css('font-weight','bold');
            $('.fileObjects').mouseleave(function (){
                $(this).css('background-color',prevBgc);
                $(this).find('.filename').css('font-weight','');
                $(this).find('.filename').html(prevName);
            });
            if(onUpload==1&&index==$(this).attr('id').split('fileTh')[1]){
                // 文件已经开始上传且this为正在上传的文件, 此后的命令将不被执行
                return
            }
            $(this).find('.filename').html($(this).find('.filename').attr('title'));
        });

        $('.fileindex').mouseenter(function (){
            if(onUpload==1){
                // 上传进程已开始 屏蔽按钮
                return
            }
            let iconNow = $(this).html();
            let titleNow = $(this).attr('title');
            $(this).html('❌');
            $(this).attr('title','删除');
            $('.fileindex').mouseleave(function (){
                $(this).html(iconNow);
                $(this).attr('title',titleNow);
                // icon置空 否则有值则会赋给其他图标
                iconNow = 0;
                titleNow = 0;
            });
        });
    }

    let flag = 0;
    let filelist = [];

    // 文件选择前事件监听
    $('#inputDiv').click(function (){$('#currentFile').trigger('click');});
    $('#currentFile').change(function (){
        flag = 0;
        filelist = []; // 先执行清空 再push
        fileChangedByInput();
    });

    function fileChangedByInput(){
        // let filelist = $('#currentFile')[0].files;
        if(flag==0){
            let filelistTMP = {};
            for(let i=0;i<$('#currentFile')[0].files.length;i++){
                filelistTMP = $('#currentFile')[0].files[i];
                filelist.push(filelistTMP);
                filelistTMP = {};
            }
            // 文件数量判断
            if(filelist.length>100){
                alert('最多同时选择100个文件!');
                return false
            }
        }
        // 缓存区更新
        appendObjects(filelist);

        // 文件更新后事件监听
        $('#emptyTrigger').click(function (){$('#currentFile').val('');$('#filenames, #buttonDiv').empty();$('#inputDiv').show();isAbort=1;onUpload=0;filelist=[];});
        $('.fileindex').click(function (){
            if(onUpload==1){
                // 上传进程已开始 屏蔽按钮
                return
            }
            let th = $(this).attr('id').split('fileindex')[1];
            filelist.splice(th,1);
            // 缓存区更新
            appendObjects(filelist);
            flag = 1; // 执行change前,flag置1,不重新读文件
            fileChangedByInput();
            flag = 0; // 执行change后,flag置0,为下次重新读文件做准备
            if(filelist.length==0){
                // 点击的文件是最后一个文件,则直接执行清空
                $('#emptyTrigger').trigger('click');
            }
        })

        // 上传按钮的监听要放在change中: change后才有#uploadtrigger
        $('#uploadTrigger').click(function (){
            if(onUpload==1){
                // 上传进程开始,屏蔽按钮
                return
            }
            // 处理文件缓存区
            $('#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; // 递归条件置零
                    onUpload = 0; // 上传开始标志位置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(){
                    onUpload = 1; // 上传标志位置1
                    ot = new Date().getTime(); // 设置上传开始时间,用以计算时间速度
                    oloaded = 0; // 设置上传开始时,已上传的文件大小为0
                    // 上传开始,缓存区加入竖线
                    $('#speedTh'+index).before('&nbsp; | &nbsp;');
                    if(filelist[index].size>104857600){
                        // 大于100M文件 插入timeremain竖线
                        $('#timeRemainTh'+index).before('&nbsp; | &nbsp;');
                    }
                }

                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(upldIconList[0]+restTime+'s');
                        $('#timeRemainTh'+index).attr('title','剩余时间');
                    }

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

                function onAbort(){
                    isAbort = 0; // 终止条件置零
                    index = 0; // 请求终止后index也必须归零,因为中止未完成递归,若中止文件index>0则第二次上传请求无法发起
                    onUpload = 0; // 上传标志位置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(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 = splitName[0];
            for(let i=1;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 35; // 不包含中文
            } else {
                return 18; // 包含中文
            }
        }
        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;
    }

    function getReminder(){
        if(location.href.indexOf('main.jsp')==-1){
            return // 不在主页面 不发请求
        }
        let url = 'http://cc.bjtu.edu.cn:81/meol/welcomepage/student/interaction_reminder.jsp?';
        let xReq = new XMLHttpRequest();
        xReq.responseType = "document";
        xReq.open('GET',url,true); // 注意,此处请求应当为异步请求 用以接收text/html数据
        xReq.addEventListener("load", onSuccess);
        xReq.addEventListener("error", onError);
        xReq.send();
        function onSuccess(){
            let reminderList = $(this.responseXML).find("li:contains('门课程有待提交作业')").find('ul>li');
            let myiframe = document.getElementsByTagName('iframe')[0].contentDocument;
            let courseTable = myiframe.getElementsByClassName('courseList');
            let courseList = $(courseTable).find('a');
            for(let i=0;i<reminderList.length;i++){
                for(let j=0;j<courseList.length;j++){
                    if(reminderList[i].innerText.indexOf(courseList[j].innerText)!=-1){
                        $(courseList[j]).css('color','red');
                    }
                    else continue
                }
            }
        }
        function onError(){
            alert('请求失败');
        }
    }

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

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址