sparkmd5+FileReader实现文件分段上传,断点续传

参考代码:https://blog.csdn.net/tangyan1207/article/details/74937645

解决了FileReader不支持readAsBinaryString方法的问题, 解决了MD5相同的问题

HTML+JS部分:

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>大文件分块上传</title>

    <style type="text/css">

        * {
            font-family: "微软雅黑";
            margin: 0;
            padding: 0;
        }



        .container {
            padding-top: 10px;
            padding-left: 10px;
        }



            .container input {
                width: 120px;
                height: 30px;
                background-color: blue;
                color: white;
                border: 0;
                line-height: 30px;
                border-radius: 5px;
                margin-right: 5px;
                outline: none;
                cursor: pointer;
            }



        #filelist {
            width: 800px;
            border: solid 1px #eee;
            border-collapse: collapse;
            margin: 10px;
        }



            #filelist td {
                border-bottom: solid 1px #eee;
                height: 30px;
                font-size: 12px;
                /*line-height:30px ;*/
                padding: 0 3px;
            }



        .filename {
            width: 200px;
            text-align: center;
        }

        .fileselectchk {
            width: 100px;
            text-align: center;
        }

        .filestatus {
            width: 100px;
            text-align: center;
        }



        .fileprogress {
            text-align: center;
        }



        .domprogress {
            width: 320px;
        }



        .domsize {
            display: block;
        }



        #tdmsg {
            text-align: center;
        }



        #fileselect {
            display: none;
        }



        span.domtime {
            display: block;
        }
    </style>



</head>



<body>

    <div class="container">

        <input type="file" name="fileselect" id="fileselect" value="" multiple />

        <input type="button" id="btnselect" value="选择上传的文件" />

        <input type="button" id="btnupload" value="开始上传" />

    </div>



    <table cellspacing="0" cellpadding="0" id="filelist">
        <tr>
            <td class="fileselectchk">选择</td>
            <td class="filename">文件名</td>
            <td class="fileprogress">进度</td>
            <td class="filestatus">状态</td>
        </tr>

    </table>
    <script src="~/Scripts/jquery-3.3.1.js"></script>
    <script src="~/Scripts/SparkMd5.js"></script>
    <script type="text/javascript">

        $("#btnselect").click(function () {

            $("#fileselect").click();

        });



        $("#fileselect").change(function () {

            var files = this.files;

            if (files.length > 0) {

                $("#trmsg").remove();

                $(files).each(function (index, item) {

                    console.log(index, item);

                    var filesize = 0;

                    if ((item.size / 1024 / 1024 / 1024) >= 1) {

                        filesize = (item.size / 1024 / 1024 / 1024).toFixed(2) + "GB"; // b=>kb=>mb=>gb

                    } else if ((item.size / 1024 / 1024 / 1024) < 1 && (item.size / 1024 / 1024) >= 1) {

                        filesize = (item.size / 1024 / 1024).toFixed(2) + "MB";

                    } else if ((item.size / 1024 / 1024) < 1 && (item.size / 1024) >= 1) {

                        filesize = (item.size / 1024).toFixed(2) + "KB";

                    } else {

                        filesize = item.size + "B";

                    }



                    var htmlstr = '<tr><td style="text-align:center"><input  type="checkbox" name="fileChk" value="' + index +'" /></td><td style="text-align:center">' + item.name + '</td><td><progress value="0" max="100" class="domprogress"></progress><span class="dompercent"> 0/' + filesize + '</span><span class="domtime">总共耗时:0 秒</span></td><td class="filestatus"><span class="domstatus">排队中</span></td></tr>';

                    $("#filelist").append(htmlstr);



                });



            }



        });

        $('fileChk').each(function (index, item) {

        });

        $("#btnupload").click(function () {



            var files = $("#fileselect")[0].files;

            $(files).each(function (index, item) {
                console.log('begin');
                console.log(index);
                yyupload(files[index], $("span.domstatus").eq(index), $("span.dompercent").eq(index), $(".domprogress").eq(index), $("span.domtime").eq(index));

            });

        });



        //文件上传

        function yyupload(file, dommsg, dompercentmb, domprogress, domtime, fn) {

            var startTime = new Date();

            var fileReader = new FileReader();

            calculate(file);

            //获取文件的加密字符串

            function calculate(file) {

                var fileReader = new FileReader();

                var chunkSize = 1024 * 1024 * 5; //每次读取5MB

                var chunksCount = Math.ceil(file.size / chunkSize); //回大于参数x的最小整数 8=》8  8.4=》9  8.5=》9 -8.5=》-8

                var currentChunk = 0; //当前块的索引

                fileReader.onload = function (e) {

                    console.log((currentChunk + 1) + "/" + chunksCount)

                    dommsg.text("正在检查文件: " + (currentChunk + 1) + "/" + chunksCount);
                    console.log(e.target.result);
                
                    currentChunk++;

                    if (currentChunk < chunksCount) {
                        console.log('currentChunk');
                        console.log(currentChunk);

                        loadNext();

                    } else {

                        var md5value = SparkMD5.ArrayBuffer.hash(e.target.result);

                        console.log("文件加密结束,密钥为:" + md5value);

                        checkfile(md5value, file); //检查服务器是否存在该文件,存在就从断点继续上传

                    }

                };

                loadNext();

                function loadNext() {

                    var start = currentChunk * chunkSize; //计算读取开始位置

                    var end = start + chunkSize >= file.size ? file.size : start + chunkSize; //计算读取结束位置

                    fileReader.readAsArrayBuffer(file.slice(start, end)); //读取为二进制字符串
                    console.log('loadNext')
                    console.log(end)

                };

             

            }



            var repeatcount = 0;

            //检查文件是否已经存在

            function checkfile(md5value, file) {
                console.log('checkfile_now')

                var fd = new FormData();

                fd.append('rquesttype', "chekcfile");

                fd.append('filename', file.name);

                fd.append('md5value', md5value);

                var xhr = new XMLHttpRequest();

                xhr.open('post', '../Files/FileUpload', true);

                xhr.onreadystatechange = function (res) {

                    if (xhr.readyState == 4 && xhr.status == 200) {

                        var jsonobj = JSON.parse(xhr.responseText); //可以将json字符串转换成json对象  //JSON.stringify(jsonobj); //可以将json对象转换成json对符串

                        console.log("继续上传的位置:" + jsonobj.startindex);

                        switch (jsonobj.flag) {

                            case "0":

                                doUpload(md5value, file, 0);

                                break;

                            case "1":

                                doUpload(md5value, file, parseInt(jsonobj.startindex));

                                break;

                            case "2":

                                secondUpload(file);

                                break;

                        }

                        repeatcount = 0;

                    } else if (xhr.status == 500) {

                        setTimeout(function () {

                            if (repeatcount < 3) {

                                checkfile(md5value, file);

                            }

                            repeatcount++;

                        }, 3000);

                    }

                }

                //开始发送

                xhr.send(fd);

            }



            //实现秒传功能

            function secondUpload(file) {
                console.log('secondUpload');

                var timerange = (new Date().getTime() - startTime.getTime()) / 1000;

                domtime.text("耗时" + timerange + "");

                //显示结果进度

                var percent = 100;

                dommsg.text(percent.toFixed(2) + "%");

                domprogress.val(percent);

                var total = file.size;

                if (total > 1024 * 1024 * 1024) {

                    dompercentmb.text((total / 1024 / 1024 / 1024).toFixed(2) + "GB/" + (total / 1024 / 1024 / 1024).toFixed(2) + "GB");

                } else if (total > 1024 * 1024) {

                    dompercentmb.text((total / 1024 / 1024).toFixed(2) + "MB/" + (total / 1024 / 1024).toFixed(2) + "MB");

                } else if (total > 1024 && total < 1024 * 1024) {

                    dompercentmb.text((total / 1024).toFixed(2) + "KB/" + (total / 1024).toFixed(2) + "KB");

                } else {

                    dompercentmb.text((total).toFixed(2) + "B/" + (total).toFixed(2) + "B");

                }



            }

            //上传文件

            function doUpload(md5value, file, startindex) {

                var reader = new FileReader();//新建一个读文件的对象

                var step = 1024 * 2000; //每次读取文件大小  200KB

                var cuLoaded = startindex; //当前已经读取总数

                var total = file.size;//文件的总大小

                //读取一段成功

                reader.onload = function (e) {

                    //处理读取的结果

                    var result = reader.result; //本次读取的数据

                    var loaded = e.loaded; //本次读取的数据长度

                    uploadFile(result, cuLoaded, function () { //将分段数据上传到服务器

                        cuLoaded += loaded; //如果没有读完,继续

                        var timerange = (new Date().getTime() - startTime.getTime()) / 1000;

                        if (total > 1024 * 1024 * 1024) {

                            dompercentmb.text((cuLoaded / 1024 / 1024 / 1024).toFixed(2) + "GB/" + (total / 1024 / 1024 / 1024).toFixed(2) + "GB");

                        } else if (total > 1024 * 1024) {

                            dompercentmb.text((cuLoaded / 1024 / 1024).toFixed(2) + "MB/" + (total / 1024 / 1024).toFixed(2) + "MB");

                        } else if (total > 1024 && total < 1024 * 1024) {

                            dompercentmb.text((cuLoaded / 1024).toFixed(2) + "KB/" + (total / 1024).toFixed(2) + "KB");

                        } else {

                            dompercentmb.text((cuLoaded).toFixed(2) + "B/" + (total).toFixed(2) + "B");

                        }



                        domtime.text("耗时" + timerange + "");

                        if (cuLoaded < total) {

                            readBlob(cuLoaded);

                        } else {

                            console.log('总共用时:' + timerange);

                            cuLoaded = total;

                            sendfinish(); //告知服务器上传完毕

                            domtime.text("上传完成,总共耗时" + timerange + "");

                        }

                        //显示结果进度

                        var percent = (cuLoaded / total) * 100;

                        dommsg.text(percent.toFixed(2) + "%");

                        domprogress.val(percent);

                    });

                }

                var k = 0;

                function sendfinish() {

                    var fd = new FormData();

                    fd.append('rquesttype', "finishupload");

                    fd.append('filename', file.name);

                    fd.append('md5value', md5value);

                    fd.append('totalsize', file.size);

                    var xhr = new XMLHttpRequest();

                    xhr.open('post', '../Files/FileUpload', true);

                    xhr.onreadystatechange = function () {

                        if (xhr.readyState == 4 && xhr.status == 200) {
                            var jsonobj = JSON.parse(xhr.responseText)
                            console.log(jsonobj);
                            console.log(fn);
                            if (fn) {

                                fn(); //如果上传成功,继续上传下一个文件

                            }

                            k = 0;

                        } else if (xhr.status == 500) {

                            setTimeout(function () {

                                if (k < 3) {

                                    sendfinish();

                                    //上传完毕的前端处理

                                }

                                k++

                            }, 3000);

                        }

                    }

                    //开始发送

                    xhr.send(fd);

                }

                var m = 0;

                //关键代码上传到服务器

                function uploadFile(result, startIndex, onSuccess) {

                    var blob = new Blob([result]);

                    //提交到服务器

                    var fd = new FormData();

                    fd.append('file', blob);

                    fd.append('rquesttype', "uploadblob");

                    fd.append('filename', file.name);

                    fd.append('md5value', md5value);

                    fd.append('loaded', startIndex);

                    var xhr = new XMLHttpRequest();

                    xhr.open('post', '../Files/FileUpload', true);

                    xhr.onreadystatechange = function () {

                        if (xhr.readyState == 4 && xhr.status == 200) {

                            m = 0;

                            if (onSuccess)

                                onSuccess();

                        } else if (xhr.status == 500) {

                            setTimeout(function () {

                                if (m < 3) {

                                    containue();

                                    m++;

                                }

                            }, 1000);

                        }

                    }

                    //开始发送

                    xhr.send(fd);

                }



                //指定开始位置,分块读取文件

                function readBlob(start) {

                    //指定开始位置和结束位置读取文件

                    var blob = file.slice(start, start + step); //读取开始位置和结束位置的文件

                    reader.readAsArrayBuffer(blob); //读取切割好的文件块

                }

                //继续

                function containue() {

                    readBlob(cuLoaded);

                }

                readBlob(cuLoaded);

            }





        }

    </script>

</body>



</html>

服务器代码(c# mvc)

       public ActionResult FileUpload()
        {
            string basefilename = HttpContext.Server.MapPath("/FileTemp/");
            HttpRequestBase req = HttpContext.Request;
            string rquesttype = req.Form["rquesttype"];
            string result = string.Empty;

            switch (rquesttype)
            {
                case "chekcfile": result = chekcfile(req); break;
                case "uploadblob": result = uploadblob(req); break;
                case "finishupload": result = finishupload(req); break;
            }

            return Content(result);
        }

        /// <summary>

        /// 结束文件上传,修改上传后的名称

        /// </summary>

        /// <param name="req"></param>
        public string finishupload(HttpRequestBase req)

        {
            string basefilename = HttpContext.Server.MapPath("/FileTemp/");
            //接收前端传递过来的参数

            var md5value = req.Form["md5value"];//文件md5加密的字符串
            var filename = req.Form["filename"];//文件的名称
            var totalsize = req.Form["totalsize"];//文件的总大小
            string fullname = basefilename + md5value + ".part";//上传的时候的名称
            string okname = basefilename + md5value + ".ok";//上传完毕之后再该目录下创建一个.ok后缀的文件,这个文件的大小为0,没有内容,不占空间,主要用于前端检查这个文件是否已经存在了
            var oldname = basefilename + filename;
            FileInfo okFile = new FileInfo(okname);
            okFile.Create();

            FileInfo oldFile = new FileInfo(oldname);
            if (oldFile.Exists)
            {
                oldFile.Delete();
            }
            FileInfo fullFile = new FileInfo(fullname);
            fullFile.MoveTo(oldname);
            var str = string.Format("{{\"data\":\"ok\"}}");

            return str;
        }





        /// <summary>

        /// 处理文件分块上传的数据

        /// </summary>

        /// <param name="req"></param>

        public string uploadblob(HttpRequestBase req)

        {
            string basefilename = HttpContext.Server.MapPath("/FileTemp/");
            long totalCount = 0;
            if (req.Files.Count <= 0)

            {

                return "获取服务器上传文件失败";

            }

            var _file = req.Files[0];

            //获取参数

            string filename = req.Form["filename"];

            string md5value = req.Form["md5value"];

            var tempfilename = md5value + ".part";

            //如果是int 类型当文件大的时候会出问题 最大也就是 1.9999999990686774G

            long loaded = Convert.ToInt64(req.Form["loaded"]);

            totalCount += loaded;

            string newname = basefilename + tempfilename;

            Stream stream = _file.InputStream;

            if (stream.Length <= 0)

                throw new Exception("接收的数据不能为空");

            byte[] dataOne = new byte[stream.Length];

            stream.Read(dataOne, 0, dataOne.Length);

            FileStream fs;

            try

            {

                fs = new FileStream(newname, FileMode.Append, FileAccess.Write, FileShare.Read, 1024);

                fs.Write(dataOne, 0, dataOne.Length);

                fs.Close();

            }

            catch (Exception ex)

            {



            }

            finally

            {

                stream.Close();
                //检查文件已经上传的大小是否等于文件的总大小


            }
            return "分段数据保存成功";
        }



        /// <summary>

        /// 检查文件是否存在

        /// </summary>

        /// <param name="req"></param>

        public string chekcfile(HttpRequestBase req)

        {
            string basefilename = HttpContext.Server.MapPath("/FileTemp/");
            var md5value = req.Form["md5value"];//得到前端传递过来的文件的mdf字符串

            var path_ok = basefilename + md5value + ".ok";

            var path_part = basefilename + md5value + ".part";

            int flag = 0;

            string json = string.Empty;

            FileInfo okFile = new FileInfo(path_ok);
            FileInfo partFile = new FileInfo(path_part);
            if (okFile.Exists)//传完了

            {

                flag = 2;

                json = string.Format("{{\"flag\":\"{0}\"}}", flag);

            }

            else if (partFile.Exists)//传了一部分

            {

                flag = 1;

                var startindex = new FileInfo(path_part).Length.ToString();

                json = string.Format("{{\"flag\":\"{0}\",\"startindex\":\"{1}\"}}", flag, startindex);

            }

            else//新文件

            {

                flag = 0;

                json = string.Format("{{\"flag\":\"{0}\",\"startindex\":\"0\"}}", flag);

            }

            return json;

        }

猜你喜欢

转载自www.cnblogs.com/cooolyuxyz/p/10693270.html