使用HTML5 FormData对象实现大文件分块上传(断点上传)功能
FormData是HTML5新增的一个对象,通过FormData对象可以组装一组用 XMLHttpRequest
发送请求的键/值对。它可以更灵活方便的发送表单数据,因为可以独立于表单使用。如果你把表单的编码类型设置为multipart/form-data ,则通过FormData传输的数据格式和表单通过submit()
方法传输的数据格式相同。具体用法参考 FormData对象的使用。
实现逻辑:客户端首先请求接口,获取一个唯一的UploadID,然后每次按照固定大小读取文件块,同时计算需要上传的总块数total,将UploadID、total、当前上传文件块的下标index、文件名称以及文件块上传到服务端,服务端根据以上参数把文件块保存到临时目录,同时判断文件块是否已经全部上传完,如果已全部上传,则需要进行合并操作,最后会返回合并后的文件信息。我的例子中,把获取UploadID的步骤跟上传放在了一起,上传接口中会判断如果UploadID为空并且index为1,则会生成一个UploadID并返回,后续每次上传都需要带上UploadID参数。
一下前端代码
前端代码
@{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>断点上传</title>
<script src="@Url.Content("~/Scripts/jquery-3.1.1.min.js")"></script>
</head>
<body>
<input id="myFile" type="file">
<button onclick="Start()">上传</button>
<button onclick="Pause()">暂停</button>
<button onclick="Continue()">继续</button>
<label>当前进度:<span id="progress"></span></label>
<script>
var uploadId = '';
var index = 1;
var pause = false; //暂停 function Start() {
index = 1;
uploadId = '';
Upload();
} function Upload() {
var files = document.getElementById('myFile').files;
if (files.length < 1) {
alert('请选择文件~');
return;
}
var file = files[0];
var totalSize = file.size;//文件大小
var blockSize = 1024 * 1024 * 2;//块大小
var blockCount = Math.ceil(totalSize / blockSize);//总块数 //创建FormData对象
var formData = new FormData();
formData.append('fileName', file.name);//文件名
formData.append('total', blockCount);//总块数
formData.append('index', index);//当前上传的块下标
formData.append('uploadId', uploadId);//上传编号
formData.append('data', null); UploadPost(file, formData, totalSize, blockCount, blockSize);
} function UploadPost(file, formData, totalSize, blockCount, blockSize) {
if (pause) {
return; //暂停
}
try {
var start = index * blockSize;
var end = Math.min(totalSize, start + blockSize);
var block = file.slice(start, end);
formData.set('data', block);
formData.set('index', index);
formData.set('uploadId', uploadId); $.ajax({
url: '',
type: 'post',
data: formData,
processData: false,
contentType: false,
success: function (res) {
block = null;
if (res.Code === 1) {
if (index === 1)
uploadId = res.UploadID; $('#progress').text((index / blockCount * 100).toFixed(2) + '%');
if (index < blockCount) {
index++;
UploadPost(file, formData, totalSize, blockCount, blockSize);
}
}
}
});
} catch (e) {
alert(e);
}
}
///暂停
function Pause() {
pause = true;
}
//继续
function Continue() {
pause = false;
Upload();
}
</script>
</body>
</html>
前端代码
服务端代码
using System;
using System.IO;
using System.Linq;
using System.Web; namespace UploadTest
{
public class UploadHelper
{ private UploadHelper()
{ } public UploadHelper(string fileRootPath)
{
if (string.IsNullOrWhiteSpace(fileRootPath))
throw new ArgumentNullException("fileRootPath", "fileRootPath is null"); FileRootPath = fileRootPath;
BlockRootPath = fileRootPath + "/blocktmp/";
}
/// <summary>
/// 块文件存储根路径
/// </summary>
private string BlockRootPath { get; set; } /// <summary>
/// 文件存储根路径
/// </summary>
public string FileRootPath { get; set; } /// <summary>
/// 分块上传
/// </summary>
public UploadResult Upload(string uploadId, int blockCount, int currIndex, string fileName, HttpPostedFileBase file)
{
try
{
if (file == null)
return new UploadResult { Msg = "请选择文件~" };
if (blockCount < )
return new UploadResult { Msg = "块数量不能小于1~" };
if (currIndex < )
return new UploadResult { Msg = "块数量小于0~" };
if (string.IsNullOrWhiteSpace(uploadId) && currIndex > )
return new UploadResult { Msg = "上传编号为空~" }; var result = new UploadResult { Code = , Msg = "上传成功~" }; //首次上传需创建上传编号
if (string.IsNullOrWhiteSpace(uploadId) || uploadId.Equals("undefind"))
uploadId = GenerateUploadId(); result.UploadID = uploadId; #region ==块处理== //块文件名称
var blockName = $"{uploadId}_{currIndex}.block";
//块文件目录路径
var blockPath = Path.Combine(BlockRootPath, uploadId);
//块文件目录对象
DirectoryInfo blockDirectoryInfo = Directory.Exists(blockPath) ? new DirectoryInfo(blockPath) : Directory.CreateDirectory(blockPath);
//块文件完整路径
var blockFullPath = Path.Combine(blockPath, blockName);
if (File.Exists(blockFullPath))
{
//块已上传,不做失败处理
return new UploadResult { Code = , Msg = "该文件块已上传~" };
} file.SaveAs(blockFullPath); #endregion #region ==块合并处理== //判断块文件是否已将上传完,上传完合并文件
if (blockDirectoryInfo.GetFiles().Count().Equals(blockCount))
{
var timestamp = DateTime.Now.ToString("yyyMMdd");
fileName = uploadId + "." + GetExtension(fileName);
var filePath = Path.Combine(FileRootPath, timestamp);
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
//完整文件存储路径
var fileFullPath = Path.Combine(filePath, fileName);
using (var fs = new FileStream(fileFullPath, FileMode.Create))
{
for (var i = ; i <= blockCount; i++)
{
var path = Path.Combine(blockPath, $"{uploadId}_{i}.block");
var bytes = File.ReadAllBytes(path);
fs.Write(bytes, , bytes.Length);
}
Directory.Delete(blockPath, true); result.FileInfo = new UploadFileInfo
{
FileName = fileName,
FilePath = Path.Combine(timestamp, fileName)
};
}
} return result;
#endregion
}
catch (Exception ex)
{
return new UploadResult { Msg = ex.Message };
}
} /// <summary>
/// 生成上传唯一编号
/// </summary>
/// <returns></returns>
public string GenerateUploadId()
{
var guid = Guid.NewGuid().ToString();
return guid.Replace("-", "");
} /// <summary>
/// 获取文件扩展名
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public string GetExtension(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName) || fileName.IndexOf(".") < )
{
return string.Empty;
}
var arr = fileName.Split('.');
return arr[arr.Length - ];
}
}
/// <summary>
/// 文件上传结果
/// </summary>
public class UploadResult
{
/// <summary>
/// 状态码 0失败 1成功
/// </summary>
public int Code { get; set; }
/// <summary>
/// 消息
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 上传编号,唯一
/// </summary>
public string UploadID { get; set; }
/// <summary>
/// 文件保存信息
/// </summary>
public UploadFileInfo FileInfo { get; set; } }
public class UploadFileInfo
{
/// <summary>
/// 文件保存名称
/// </summary>
public string FileName { get; set; }
/// <summary>
/// 文件保存路径
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// 文件MD5值
/// </summary>
public string MD5 { get; set; }
}
}
UploadHelper
Controller代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace UploadTest.Controllers
{
[RoutePrefix("upload")]
public class UploadController : Controller
{
[Route]
[HttpGet]
public ActionResult Index()
{
return View();
}
[Route]
[HttpPost]
public ActionResult Upload(string uploadId,int total,int index,string fileName)
{
var helper = new UploadHelper("D:\\Upload");
var result = helper.Upload(uploadId, total, index, fileName, Request.Files[]); return Json(result);
}
}
}
使用HTML5 FormData对象实现大文件分块上传(断点上传)功能的更多相关文章
- php大文件分块上传断点续传demo
前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...
- .Net之使用Jquery Ajax通过FormData对象异步提交图片文件到服务端保存并返回保存的图片路径
前言: 首先对于图片上传而言,在我们的项目开发中可以说出现的频率是相当的高的.这篇文章中,我将要描述的是在我们.Net中如何使用Jquery Ajax通过FormData对象异步提交图片文件到后台保存 ...
- JS原生上传大文件显示进度条-php上传文件
JS原生上传大文件显示进度条-php上传文件 在php.ini修改需要的大小: upload_max_filesize = 8M post_max_size = 10M memory_li ...
- 使用PHP和HTML5 FormData实现无刷新文件上传教程
无刷新文件上传是一个常见而又有点复杂的问题,常见的解决方案是构造 iframe 方式实现. 在 HTML5 中提供了一个 FormData 对象 API,通过 FormData 可以方便地构造一个表单 ...
- 利用blob对象实现大文件分片上传
首先说分片上传,我们在进行文件上传的时候,因为服务器的限制,会限制每一次上传到服务器的文件大小不会很大,这个时候我们就需要把一个需要上传的文件进行切割,然后分别进行上传到服务器. 假如需要做到这一步, ...
- js大文件分块上传断点续传demo
文件夹上传:从前端到后端 文件上传是 Web 开发肯定会碰到的问题,而文件夹上传则更加难缠.网上关于文件夹上传的资料多集中在前端,缺少对于后端的关注,然后讲某个后端框架文件上传的文章又不会涉及文件夹. ...
- HTML5 FormData对象
利用FormData对象,你可以使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest发送这个"表单". 创建一个FormData对象 你可以先创建一个空的F ...
- java大文件分块上传断点续传demo
第一点:Java代码实现文件上传 FormFile file = manform.getFile(); String newfileName = null; String newpathname = ...
- springboot集成websocket实现大文件分块上传
遇到一个上传文件的问题,老大说使用http太慢了,因为http包含大量的请求头,刚好项目本身又集成了websocket,想着就用websocket来做文件上传. 相关技术 springboot web ...
随机推荐
- WPF的一些感悟
第一天在博客园写东西,只写一些自己对WPF稚嫩的理解和感悟. 1.Code Snippet代码简写工具 可以创建自己的代码模板管理器——>>>工具菜单,代码片段管理器 考出现有的+更 ...
- flask笔记一
最近学习flask,由于web开发方面接触的并不是很多,所以看官方文档有点焦头烂额,好多的概念不理解. <Flask web 开发>比较基础,先用这本书做个入门. 1.Flask实例化对象 ...
- 大话Python中*args和**kargs的使用
对于初学者来说,看到*args和**kargs就头大,到底它们有何用处,怎么使用?这篇文章将为你揭开可变参数的神秘面纱 1.*args 实质就是将函数传入的参数,存储在元组类型的变量args当中 de ...
- 使用 FLASH DATABASE 恢复误删除的用户
场景描述 误 drop 了生产库中的用户 U1 U1 用户下面有 3 张表(T1-T3),表中数据如下所示: SQL> conn u1/u1 Connected. SQL> select ...
- 关闭数据库下的所有连接操作 sql存储过程
use master go )) as begin ),) declare @spid int set @sql='declare getspid cursor for select spid fro ...
- 无法为具有固定名称“MySql.Data.MySqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“MySql.Data.MySqlClient.MySqlProviderServices,MySql.Data.Entity.EF6”
"System.InvalidOperationException"类型的未经处理的异常在 mscorlib.dll 中发生 其他信息: 无法为具有固定名称"MySql. ...
- System.Runtime.Serialization.SerializationException”类型的未经处理的异常在 System.Runtime.Serialization.dll 中发生
异常信息: “System.Runtime.Serialization.SerializationException”类型的未经处理的异常在 System.Runtime.Serialization. ...
- 9天C#转Java学习过程,自己记录一下
其实没有完整的9天,就是连续每天花点时间,过程so frustrated,踩坑无数...下面是学习过程的记录 第1天 开始正式学习JavaEE,已完成: 1. Tomcat安装: 2. Tomcat配 ...
- GC机制总结
一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...
- css之选择器总结
首先我们来看下有哪些选择器??? 一.基础选择器: html标签选择器:通过html标签来选择元素. ①所有的html标签都可以当做选择器. ②无论标签藏多深都会被选中. ③选择的是所有的标签而不是某 ...