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);
}
}
}