断点续传、支持超大文件(无需安装客户端插件)

来源:互联网 发布:网络被限制了怎么办 编辑:程序博客网 时间:2024/06/06 12:49

首先声明,这不是一个理论性的文章,是经过的实践证明的,文章的结尾会附带demo,demo是比较简单的,没有经过封装,一看就懂

 

准备工作:

plupload,版本应该是随意的,我使用的哪个版本也忘记了。

简单的介绍plupload,plupload就是一个在客户端进行了文件切割之后上传的javascript库,它有5种不同的上传技术,看浏览器的支持情况,或许会用html5、fllash之类的技术,但是这些都是内部实现的,我们无需关心,后台是用php实现,也能找到asp.net相关的例子。

早期使用它进行友好的进度显示等...当然现在经过这几年的jquery革命,产生的类似插件也多如牛毛,本来只是重点讲解思路。理论上用其它插件依然能办到。

 

思路:在文件切割分块之后、上传之前,通过ajax去检测服务器上已经存在的文件区块,然后设置plupload的起始区块,以达到我们断点续传的目地,呵呵,是不是很简单?看代码

 

首先我上一个asp.net mvc的后台代码,用于保存上传文件与读取已存在区块的功能。

        /// <summary>        /// 文件上传        /// </summary>        /// <returns></returns>         public JsonResult plupload(string name)        {            string[] dd = Request.Headers.AllKeys;            string msg = string.Empty;            int chunk = Convert.ToInt32(Request["chunk"]); //当前分块            int chunks = Convert.ToInt32(Request["chunks"]);//总的分块数量            long hcouns = 0;            foreach (string upload in Request.Files)            {                if (upload != null && upload.Trim() != "")                {                    string path = AppDomain.CurrentDomain.BaseDirectory + "Temp\\";                    if (!Directory.Exists(path))    //判断给定的路径上是否存在该目录                    {                        Directory.CreateDirectory(path);    //不存在则创建该目录                    }                    System.Web.HttpPostedFileBase postedFile = Request.Files[upload];   //获取客户端上载文件的集合                    string filename1 = Path.GetFileName(postedFile.FileName);   //获取客户端上传文件的名称及后缀                    string filename = name; //                    string newFileName = filename;                    if (chunks > 1)                    {                        newFileName = chunk + "_" + filename;   //按文件块重命名块文件                    }                    string fileNamePath = path + newFileName;   //将块文件和临时文件夹路径绑定                    if (chunks > 0)                    {                        for (int i = 0; i < chunks; i++)                        {                            //检测已存在磁盘的文件区块                            if (!System.IO.File.Exists(path+i.ToString() + "_" + filename) && i != chunk)                            {                                hcouns = i * postedFile.ContentLength;                                break;                              }                        }                    }                    postedFile.SaveAs(fileNamePath);    //保存上载文件内容                    if (chunks > 1 && chunk + 1 == chunks)    //判断块总数大于1 并且当前分块+1==块总数(指示是否为最后一个分块)                    {                        using (FileStream fsw = new FileStream(path + filename, FileMode.Create, FileAccess.Write))                        {                            BinaryWriter bw = new BinaryWriter(fsw);                            // 遍历文件合并                             for (int i = 0; i < chunks; i++)                            {                                bw.Write(System.IO.File.ReadAllBytes(path + i.ToString() + "_" + filename));    //打开一个文件读取流信息,将其写入新文件                                System.IO.File.Delete(path + i.ToString() + "_" + filename);        //删除指定文件信息                                bw.Flush(); //清理缓冲区                            }                        }                    }                }            }            return Json(new { jsonrpc = "2.0", result = "", id = "id", hcount = "" + hcouns.ToString() + "" });        }        /// <summary>        /// 检测文件已有区块        /// </summary>        /// <returns></returns>        [HttpPost]        public JsonResult checkplupload()        {            string fileName = Request["fileName"].ToString();            //文件名称            long Size = Request["size"] == null ? 0 : Convert.ToInt64(Request["size"]);            //文件的分块大小            int fileCount = Request["maxFileCount"] == null ? 0 : Convert.ToInt32(Request["maxFileCount"]);            //文件一共的分块数量。比如1G以20M分块,则有50块。            //上面变量通过加载文件的时候通过ajax方式提交过来            long hcouns = 0;            string path = AppDomain.CurrentDomain.BaseDirectory + "Temp\\";            if (fileCount > 0)            {                for (int i = 0; i < fileCount; i++)                {                    //检测已存在磁盘的文件区块                    if (!System.IO.File.Exists(path + i.ToString() + "_" + fileName))                    {                        //你懂的,如果服务器上不存在如 i_文件名这个文件,那证明客户端应该从这个字节开始往服务器上传。                        hcouns = i * Size;                        //服务器已存在区块的总字节数                        break;                    }                }            }            //返回到客户端            return Json(new { result = hcouns });        }


上面这个代码应该 是通俗易懂的,第一个方法用于接收文件上次,第二个则是上传之前的检测。

接着我们应该在什么地方来使用它,用过pupload的都知道它里面存在着很多回调函数,包括上传前、分块完成之后.....很多!

那么到底应该在哪个回调函数的时候进行检测才能达到我们的目地?通过API查找,我们找到了一个

 

FilesAdded当文件添加到上传队列后触发

监听函数参数:(uploader,files)

uploader为当前的plupload实例对象,files为一个数组,里面的元素为本次添加到上传队列里的文件对象

接着上我修改之后的代码:

uploader.bind('FilesAdded', function(up, files) {            var DATA={                fileName:files[0].name,                size:up.settings.chunk_size,                maxFileCount:Math.ceil(files[0].size/up.settings.chunk_size)              };            $AjaxPost("/Home/checkplupload",DATA,function(dy){                files[0].loaded=dy.result;            });self._trigger('selected', null, { up: up, files: files } );// re-enable sortableif (self.options.sortable && $.ui.sortable) {self._enableSortingList();}self._trigger('updatelist', null, { filelist: self.filelist });if (self.options.autostart) {// set a little delay to make sure that QueueChanged triggered by the core has time to completesetTimeout(function() {self.start();}, 10);}});


 

代码中的DATA为我自己构造的一个参数,$AjaxPost是我自己写的一个ajax请求方法,目标地址就是我们刚刚第一个c#代码中的检测文件区块部分。(实际这里应该是一个for循环去请求,因为它是支持多文件同时上传的,这里用的是单文件,自己改改即可)。最后在ajax请求完成的回调中,我设置了files[0].loaded,也就是设置了文件的开始区块。

直接的效果就是:假设我上传一个5GB的文件,2M/块进行上传,可能会在磁盘上形成2500多个2M大小的区块,如果当用户上传到2000块的时候,断网了....传统的方式就悲剧了,联网后又得重新上传,而经过这样的改造之后,在用户再此上传时首先会检测文件咋服务器上是否已经存在区块了,如果已经存在了2000块,那么就会把文件的区块索引改成2000,直接从2001块开始上传。

 

此解决方案本来已在项目中应用,当然:真正应用到项目时可能会和数据库、文件MD5等许多关联,关于其它问题,不在本文讨论范围内。

 

UpLoadFile MVC完整Demo支持断点续传

0 0
原创粉丝点击