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

首先声明,这不是一个理论性的文章,是经过的实践证明的,文章的结尾会附带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 sortable
			if (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 complete
				setTimeout(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支持断点续传

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值