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等属性也都在表单里。
然后是服务端,没什么新鲜的,完全是在接收一个普通的文件:
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,发个请求询问服务器文件是否已存在,如果存在就不要重复上传了,这样就实现了“极速上传”以及“断点续传”。
- HTML 教程
- HTML 简介
- html div 标签介绍
- html span 标签介绍
- html a 超链接标签
- HTML Br换行标签介绍
- HTML P段落标签介绍
- HTML br与p标签区别
- Html H 标题标签
- html px em pt长度单位
- HTML form 标签
- HTML radio 单选框
- HTML B 加粗标签
- HTML strong加粗粗体标签
- HTML em 强调标签
- HTML i 斜体标签
- HTML u下划线标签
- HTML s 删除线标签
- Html img 图片标签
- Html上标注sup与下标注sub标签
- HTML nobr 禁止换行标签
- HTML hr 水平线标签
- HTML label 标签
- HTML input 标签
- HTML textarea 标签
- HTML select下拉列表标签
- HTML checkbox 多选框
- HTML font color 标签
- HTML iframe 框架标签
- HTML Table 表格
- HTML dl dt dd 标签
- HTML ol li有序列表标签
- HTML ul li 无序列表标签
- HTML 注释
- CSS 教程
- CSS 简介
- CSS 语法
- CSS Id 和 Class选择器
- CSS 样式的创建
- CSS background 背景介绍
- CSS 文本样式
- CSS font 字体
- CSS A 链接
- CSS ul ol列表样式
- CSS TABLE 样式
- CSS 框模型
- CSS border 边框
- CSS Outlines 轮廓
- CSS 外边距 Margin
- CSS Padding 内边距
- CSS 分组和嵌套选择器
- CSS 尺寸 (Dimension)
- CSS Display 属性
- CSS Position 定位
- CSS Float 浮动
- CSS 水平对齐(Horizontal Align)
- CSS 组合选择符
- CSS 伪类
- CSS 伪元素
- CSS 导航栏
- CSS 下拉菜单
- CSS 图片廊
- CSS 图像透明/不透明
- CSS sprite 图像拼合技术
- CSS 媒体类型
- CSS 属性选择器
- CSS 实例
- python中常见错误及解决方法
- tensorflow 2.0模式下训练的模型转成 tf1.x 版本的pb模型实例
- Python如何实现后端自定义认证并实现多条件登陆
- safari下载文件自动加了html后缀问题
- yii2 上传图片的示例代码
- DataFrame.groupby()所见的各种用法详解
- keras训练曲线,混淆矩阵,CNN层输出可视化实例
- Pandas缺失值2种处理方式代码实例
- ThinkPHP5 的简单搭建和使用详解
- PyInstaller运行原理及常用操作详解
- php使用curl模拟浏览器表单上传文件或者图片的方法
- Python轻量级web框架bottle使用方法解析
- 详解在YII2框架中使用UEditor编辑器发布文章
- 基于python和flask实现http接口过程解析
- Python xpath表达式如何实现数据处理