改写文件上传支持断点续传-前端JS代码

前端代码

<!DOCTYPE html>
<html lang="en">
 <head> 
  <meta charset="UTF-8" /> 
  <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 
  <meta http-equiv="X-UA-Compatible" content="ie=edge" /> 
  <link rel="stylesheet" href="./bootstrap/bootstrap.min.css" /> 
  <link rel="stylesheet" href="./bootstrap/bootstrap-theme.min.css" /> 
<style>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background:#f5f5f5;
}

.row_input{
    width: 100%;
    max-width: 400px;
  height: 39px;
  margin: 15px auto;
  background: #fefefe;
  border: 1px solid #cecece;
  font-size: 12px;
  font-family: sans-serif;
  color: #888;
  border-radius: 4px;
  cursor: pointer;
  overflow: hidden; 
  padding:0!important;
  position:relative;
}

.input-caption{
  display: block;
  height: 100%;
    line-height:36px;
  padding-left: 10px;
  text-overflow: ellipsis;
  overflow: hidden;
  font-size:14px;
  font-weight: normal;
  padding-right:100px
}
.close_btn{
    width:100%;
    margin:0 auto;
    max-width:400px;
    display: block;
    height: 100%;
    padding:0;
    border: 1px solid #ccc;
    line-height: 36px;
    border-radius: 4px;
    color: #666666;
    margin-top: 15px;
    text-align: center;
    background-color: #fefefe;
    background-image: -webkit-gradient(linear,0 0,0 100%,from(#fefefe),to(#f1f1f1));
    background-image: -webkit-linear-gradient(top,#fefefe,#f1f1f1);
    background-image: -o-linear-gradient(top,#fefefe,#f1f1f1);
    background-image: linear-gradient(to bottom,#fefefe,#f1f1f1);
    background-image: -moz-linear-gradient(top,#fefefe,#f1f1f1);
    -webkit-transition: all .1s ease-out;
    -moz-transition: all .1s ease-out;
    -o-transition: all .1s ease-out;
    transition: all .1s ease-out;   
    cursor:pointer
    }
.input-button{
    position:absolute;
    right:0;
    top:0;  
    display: block;
    height: 100%;
    padding:0;
    border-left: 1px solid #ccc;
    color: #666666;
    text-align: center;
    background-color: #fefefe;
    background-image: -webkit-gradient(linear,0 0,0 100%,from(#fefefe),to(#f1f1f1));
    background-image: -webkit-linear-gradient(top,#fefefe,#f1f1f1);
    background-image: -o-linear-gradient(top,#fefefe,#f1f1f1);
    background-image: linear-gradient(to bottom,#fefefe,#f1f1f1);
    background-image: -moz-linear-gradient(top,#fefefe,#f1f1f1);
    -webkit-transition: all .1s ease-out;
    -moz-transition: all .1s ease-out;
    -o-transition: all .1s ease-out;
    transition: all .1s ease-out;   
    cursor:pointer
}

.wrap {
    width: 100px;
    height: 36px;
    background-color: red;
    text-align: center;
    border-radius:0;
    border:0;
    padding:0;
}

.wrap p {
    width: 100%;
    height: 100%;
    line-height: 36px;
    text-align: center;
}

#file {
    position: absolute;
    left: 0;
    top: 0;
    width: 100px;
    height: 36px;
    display: block;
    opacity: 0;
}

.progress {
    position: relative;
}

.progress-bar {
    transition: width .3s ease
}

.progress .value {
    position: absolute;
    color: #FF9800;
    left: 50%;
}

.container {
    width: ;
}

.container label{
    display:block;
    cursor:pointer;
    }

.row {
    padding: 10px;
    margin:0;
}

.hidden {
    display: none;
}

.loading_pro{
    margin:0 auto;
    max-width:400px;
    border: 1px solid #C5C5C5;
    box-shadow: 0 0 3px #ccc;
    -webkit-box-shadow: 0 0 3px #ccc;
    background:white;
    margin-top:15px;
    padding-bottom:10px;
    }
.loading_pro h5{
    text-align:center;
    line-height:24px;
    font-size:16px;
    margin-bottom:0;
    }
    
.dotting {
    display: inline-block; width: 10px; min-height: 2px;
    padding-right: 2px;
    border-left: 2px solid currentColor; border-right: 2px solid currentColor;   
    background-color: currentColor; background-clip: content-box;
    box-sizing: border-box;
    animation: dot 4s infinite step-start both;
    *zoom: expression(this.innerHTML = '...'); /* IE7 */
}
.dotting:before { content: '...'; } /* IE8 */
.dotting::before { content: ''; }
:root .dotting { margin-left: 2px; padding-left: 2px; } /* IE9+ */

@keyframes dot {
    25% { border-color: transparent; background-color: transparent; }          /* 0个点 */
    50% { border-right-color: transparent; background-color: transparent; }    /* 1个点 */
    75% { border-right-color: transparent; }                                   /* 2个点 */
}
</style> 
  <title>上传文件</title> 
 </head> 
 <body> 
  <div class="container"> 

  
    
   <label for="file">
    <div class="row_input"> 
        <div class="input-caption" id = "input-caption">
         选择文件上传
        </div> 
        <div class="input-button"> 
         <div class="wrap btn btn-default"> 
          <input type="file" id="file" /> 
          <p class="">上传文件</p> 
         </div> 
        </div> 
    </div>
   </label>
    <div class="loading_pro">
        <h5 id="select" style="display:">请选择视频文件(*.mp4,*.flv),图片(*.png, *.jpg, *.gif)</h5>              
        <h5 id="uping" style="display:none">上传中<span class="dotting" style="display: "></span></h5> 
        <h5 id="upend" style="display:none">上传成功</h5>
        
        
        <div class="row" id="process1" style="display: none"> 
            <div class="col-md-4">
            校验文件进度
            </div> 
            <div class="col-md-8"> 
                <div class="progress"> 
                    <div id="checkProcessStyle" class="progress-bar" style="width:0%"></div> 
                    <p id="checkProcessValue" class="value">0%</p> 
                </div> 
            </div> 
        </div> 
        <div class="row" id="process2" style="display: none"> 
        <div class="col-md-4">
        上传文件进度
        </div> 
        <div class="col-md-8"> 
        <div class="progress"> 
        <div id="uploadProcessStyle" class="progress-bar" style="width:0%"></div> 
        <p id="uploadProcessValue" class="value">0%</p> 
        </div> 
        </div> 
        </div>
    </div> 
            <div class="close_btn">
                返回
            </div>
  </div> 
  <script src="./lib/jquery-1.10.2.min.js"></script> 
  <script src="./bootstrap/bootstrap.min.js"></script> 
  <script src="./lib/spark-md5.min.js"></script> 
  <script>
        let baseUrl = 'http://[这里写服务器ip]'
        let chunkSize = 5 * 1024  * 1024            
        let fileSize = 0
        let file = null
        let hasUploaded = 0
        let chunks = 0
        // spark = new SparkMD5.ArrayBuffer()
        $("#file").on('change', function () {       
            file = this.files[0]
            fileSize = file.size;
            responseChange(file)
        })

        // 0.响应点击
        async function responseChange(file) {
            

            document.getElementById("select").style.display="none";
            document.getElementById("uping").style.display="";
            
            
            //document.getElementById("sp").style.display="none";
            // 第一步:按照 修改时间+文件名称+最后修改时间-->MD5
            // 显示文件校验进度
            $("#process1").slideDown(200)
            
            //var a = document.getElementById("input-caption").innerText;
           // var a = document.getElementsByClassName("input-caption");
           // $(".input-caption").html() = "bbbbbbb";
          y = document.getElementById("input-caption");
            y.innerHTML = file.name;           
            // 开始校验
            let fileMd5Value = await md5File(file) // 生成
            //alert("fileMd5Value =" + fileMd5Value);
            // 第二步:校验文件的MD5
            let result = await checkFileMD5(file.name, fileMd5Value);       
                
            var RESULT = eval(result);                           
            // 如果文件已存在, 就秒传
            if (RESULT.filename) {
                //alert('文件已秒传');
                document.getElementById("process1").style.display="none";
                document.getElementById("process2").style.display="none";  
                        
                var y = document.getElementById("upend");
                y.innerHTML = "上传已完成";
                document.getElementById("select").style.display="none";
                document.getElementById("uping").style.display="none";
                document.getElementById("upend").style.display="";
                
                return;
            }
             let exit = false
            // alert("result.chunkList = " + result.chunkList); 
             
                
            // 显示文件上传进度
            $("#process2").slideDown(200)
        
            
        
            // 第三步:检查并上传MD5
           // alert("result.chunkList = " + result.chunkList);           
             // 第三步:检查并上传MD5          
           await checkAndUploadChunk(fileMd5Value, RESULT.chunkList, file);
           
          
           await notifyServer(fileMd5Value, file, chunks);  

            //alert("hasUploaded" + hasUploaded); // Compute hash
            // 第四步: 通知服务器所有分片已上传完成
           // notifyServer(fileMd5Value)
        }

        // 1.修改时间+文件名称+最后修改时间-->MD5
        function md5File(file) {
            return new Promise((resolve, reject) => {
                var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
                    //chunkSize = 2097152, // Read in chunks of 2MB
                    chunkSize = file.size / 100,
                    //chunks = Math.ceil(file.size / chunkSize),
                    chunks = 100,
                    currentChunk = 0,
                    spark = new SparkMD5.ArrayBuffer(),
                    fileReader = new FileReader();

                fileReader.onload = function (e) {
                    //console.log('read chunk nr', currentChunk + 1, 'of', chunks);
                    spark.append(e.target.result); // Append array buffer
                    currentChunk++;

                    if (currentChunk < chunks) {
                        loadNext();
                    } else {
                        let cur = +(new Date())
                        //console.log('finished loading');
                        // alert(spark.end() + '---' + (cur - pre)); // Compute hash
                        let result = spark.end()
                        resolve(result)
                    }
                };

                fileReader.onerror = function () {
                    //console.warn('oops, something went wrong.');
                };

                function loadNext() {
                    var start = currentChunk * chunkSize,
                        end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

                    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
                    $("#checkProcessStyle").css({
                        width: (currentChunk + 1) + '%'
                    })
                    $("#checkProcessValue").html((currentChunk + 1) + '%')
                    // $("#tip").html(currentChunk)
                }
                loadNext();
            })
        }
        
        //2.校验文件的MD5
        function checkFileMD5(fileName, fileMd5Value) {
            return new Promise((resolve, reject) => {
                //let url = baseUrl + '/check/file?fileName=' + fileName + "&fileMd5Value=" + fileMd5Value
                /*
                    检查文件是否存在,如果整个文件以存在或不存在返回文件名,如果是存在文件块返回文件块列表(json格式),
                    filename            上传文件名
                    fileMd5Value        上传文件MD5值
                    method              要调用的方法名
                
                */
                let url = baseUrl + '/upload.cgi?filename=' + fileName + "&fileMd5Value=" + fileMd5Value + "&method=" + "check";
                $.getJSON(url, function (data) {
                    resolve(data)   
                })              
            })          
        }
        
        
        // 3.上传chunk
        async function checkAndUploadChunk(fileMd5Value, chunkList, file) {         
            chunks = Math.ceil(fileSize / chunkSize)    //片段数等于文件总大小除去一个片段的大小   
                    
            //let exit = false;
            var List =  eval(chunkList);
    
            var hasUploaded = 0;
            var len = 0;
            if((chunkList != null) && (chunkList != "") && (chunkList != undefined))
            {
                hasUploaded = List.length   
                len = List.length;
            }
            
                    
            /*获取json文件列表,判断文件列表中的 文件*/           
           //console.log("chunks: "+ chunks + " List: "+ List.length + " hasUploaded = " + hasUploaded);
     
            for (var i = 0; i < chunks; i++) 
            {                           
                //let exit = chunkList.indexOf(i + "") > -1 
                    
                let exit  = false;
                if(i < len)
                {                   
                    if((chunkList != null) && (chunkList != "") && (chunkList != undefined) && (chunkList[i].filename != null) && (chunkList[i].filename != undefined) && (chunkList[i].filename != "") )
                    {                                       
                        for(var j =0; j < len; j++)
                        {
                            if(chunkList[j].filename.indexOf(i + '') > -1)
                            {
                                exit = true;                                
                            }                           
                        }                                                                                           
                    }                   
                }
                  
                                                                                 
              // 如果已经存在, 则不用再上传当前块
               if (!exit)  
               {                                                
                   let index = await upload(i, fileMd5Value, chunks, file);
                    hasUploaded++;
                                                            
                    let radio = Math.floor((hasUploaded / chunks) * 100);
                    $("#uploadProcessStyle").css({
                        width: radio + '%'
                    })
                    $("#uploadProcessValue").html(radio + '%');
                }
            }
        }

        // 3-2. 上传chunk
        function upload(i, fileMd5Value, chunks, file) {
            return new Promise((resolve, reject) => {
                //构造一个表单,FormData是HTML5新增的
                let end = (i + 1) * chunkSize >= file.size ? file.size : (i + 1) * chunkSize
                let form = new FormData()
                form.append("data", file.slice(i * chunkSize, end)) //file对象的slice方法用于切出文件的一部分
                form.append("total", chunks) //总片数
                form.append("index", i) //当前是第几片     
                form.append("fileMd5Value", fileMd5Value)
                /*
                    传输文件块
                    method   请求方法名
                
                */
                
                
                $.ajax({
                    url: baseUrl + '/upload.cgi?method=' + "upload" + "&filename=" + file.name + "&fileMd5Value=" + fileMd5Value, 
                    
                    type: "POST",
                    data: form, //刚刚构建的form数据对象
                    async: true, //异步
                    processData: false, //很重要,告诉jquery不要对form进行处理
                    contentType: false, //很重要,指定为false才能形成正确的Content-Type
                    success: function (data) {
                        resolve(data.desc)
                    }
                })              
            })
        }

        // 第四步: 通知服务器所有分片已上传完成
        function notifyServer(fileMd5Value, file , chunks) {
            document.getElementById("process1").style.display="none";
            document.getElementById("process2").style.display="none";
            //document.getElementById("sp").style.display="none";
            //var y = document.getElementById("uping");
            //y.innerHTML = "上传成功";
            
            document.getElementById("select").style.display="none";
            document.getElementById("uping").style.display="none";
            document.getElementById("upend").style.display="";
            
            
            /*
                发送合并请求
                fileMd5Value      文件的MD5值
                fileName          文件名
                size              文件大小
                method            请求的方法名
            */
            
            let url = baseUrl + '/upload.cgi?fileMd5Value=' + fileMd5Value + "&filename=" + file.name + "&size=" + file.size  + "&method=" + "merge" + "&chunks=" + chunks;
            $.getJSON(url, function (data) {                
                //alert('上传成功')
            })
        }

        function getDate() {
            let d = new Date()
            return d.getMinutes() + ':' + d.getSeconds() + ' ' + d.getMilliseconds()
        }
    </script>  
 </body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容