首先声明,这不是一个理论性的文章,是经过的实践证明的,文章的结尾会附带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.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等许多关联,关于其它问题,不在本文讨论范围内。