视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
h5在网页中上传超大文件教程实例
2020-11-27 15:09:28 责编:小采
文档
利用HTML5分片上传超大文件

在网页中直接上传大文件一直是个比较头疼的问题,主要面临的问题一般包括两类:一是上传时间长中途一旦出错会导致前功尽弃;二是服务端配置复杂,要考虑接收超大表单和超时问题,如果是托管主机没准还改不了配置,默认只能接收小于4MB的附件。

比较理想的方案是能够把大文件分片,一片一片的传到服务端,再由服务端合并。这么做的好处在于一旦上传失败只是损失一个分片而已,不用整个文件重传,而且每个分片的大小可以控制在4MB以内,服务端不用做任何设置就可适应。

常用的解决方案是RIA,以flex为例,通常是利用FileReference.load方法加载文件得到ByteArray,然后分片构造表单(flash的高版本不允许直接访问文件)。不过这个load方法只能加载较小的文件,大约不超过300MB,因此适用性不是很强。

好在现在有了HTML5,我们可以直接构造分片了,这是一个非常喜人的进步,只可惜目前适用面不广(IE啊IE,真是恨你恨得牙痒痒)。

言归正传,来看一个DEMO吧,基于ASP.Net MVC3,只是示例,很多问题做了简化处理。

主要是客户端,新特性都体现在这里:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
 <meta charset="utf-8">
 <title>HTML5大文件分片上传示例</title>
 <script src="../Scripts/jquery-1.11.1.min.js"></script>
 <script>
 var page = {
 init: function(){
 $("#upload").click($.proxy(this.upload, this));
 },
 
 upload: function(){
 var file = $("#file")[0].files[0], //文件对象
 name = file.name, //文件名
 size = file.size, //总大小
 succeed = 0;
 
 var shardSize = 2 * 1024 * 1024, //以2MB为一个分片
 shardCount = Math.ceil(size / shardSize); //总片数
 
 for(var i = 0;i < shardCount;++i){
 //计算每一片的起始与结束位置
 var start = i * shardSize,
 end = Math.min(size, start + shardSize);
 //构造一个表单,FormData是HTML5新增的
 var form = new FormData();
 form.append("data", file.slice(start,end)); //slice方法用于切出文件的一部分
 form.append("name", name);
 form.append("total", shardCount); //总片数
 form.append("index", i + 1); //当前是第几片
 
 //Ajax提交
 $.ajax({
 url: "../File/Upload",
 type: "POST",
 data: form,
 async: true, //异步
 processData: false, //很重要,告诉jquery不要对form进行处理
 contentType: false, //很重要,指定为false才能形成正确的Content-Type
 success: function(){
 ++succeed;
 $("#output").text(succeed + " / " + shardCount);
 }
 });
 }
 }
 };
 $(function(){
 page.init();
 });
 </script>
</head>
<body>
 <input type="file" id="file" />
 <button id="upload">上传</button>
 <span id="output" style="font-size:12px">等待</span>
</body>
</html>

这里的slice方法和FormData都是html5之前不存在的。通过这样的方法,我们的表单构造出来是这样的,抓包看看:

可以看到构造出来的Content-Type是multipart/form-data,也就是符合RFC标准的那个最传统的文件上传表单。另外我们同时传输的name、total等属性也都在表单里。

然后是服务端,没什么新鲜的,完全是在接收一个普通的文件:

[HttpPost]
public ActionResult Upload()
{
 //从Request中取参数,注意上传的文件在Requst.Files中
 string name = Request["name"];
 int total = Convert.ToInt32(Request["total"]);
 int index = Convert.ToInt32(Request["index"]);
 var data = Request.Files["data"];
 
 //保存一个分片到磁盘上
 string dir = Server.MapPath("~/Upload");
 string file = Path.Combine(dir, name + "_" + index);
 data.SaveAs(file);
 
 //如果已经是最后一个分片,组合
 //当然你也可以用其它方法比如接收每个分片时直接写到最终文件的相应位置上,但要控制好并发防止文件锁冲突
 if(index == total)
 {
 file = Path.Combine(dir, name);
 var fs = new FileStream(file, FileMode.Create);
 for(int i = 1;i <= total;++i)
 {
 string part = Path.Combine(dir, name + "_" + i);
 var bytes = System.IO.File.ReadAllBytes(part);
 fs.Write(bytes, 0, bytes.Length);
 bytes = null;
 System.IO.File.Delete(part);
 }
 fs.Close();
 }
 
 //返回是否成功,此处做了简化处理
 return Json(new { Error = 0 });
}

上面的DEMO很多问题是简化处理的,比如没做什么异常处理,客户端也没有判断服务端是否出错重试一类的,各位可以自己完善。

在上面的基础上,我们可以做很多功能上的扩展,比如我们可以控制所有分片是顺序上传还是并发上传,以适用不同应用。再比如我们可以在整体文件上传前以及分片上传前都先计算一下相应的HASH,发个请求询问服务器文件是否已存在,如果存在就不要重复上传了,这样就实现了“极速上传”以及“断点续传”

【相关推荐】

1. 免费h5在线视频教程

2. HTML5 完整版手册

3. php.cn原创html5视频教程

下载本文
显示全文
专题