FormData是HTML5新增的一个对象,通过FormData对象可以组装一组用 发送请求的键/值对。它可以更灵活方便的发送表单数据,因为可以独立于表单使用。如果你把表单的编码类型设置为multipart/form-data ,则通过FormData传输的数据格式和表单通过 方法传输的数据格式相同。具体用法参考 。
实现逻辑:客户端首先请求接口,获取一个唯一的UploadID,然后每次按照固定大小读取文件块,同时计算需要上传的总块数total,将UploadID、total、当前上传文件块的下标index、文件名称以及文件块上传到服务端,服务端根据以上参数把文件块保存到临时目录,同时判断文件块是否已经全部上传完,如果已全部上传,则需要进行合并操作,最后会返回合并后的文件信息。我的例子中,把获取UploadID的步骤跟上传放在了一起,上传接口中会判断如果UploadID为空并且index为1,则会生成一个UploadID并返回,后续每次上传都需要带上UploadID参数。
一下前端代码
前端代码
1 @{ 2 Layout = null; 3 } 4 5 6 7 8 9 10断点上传 11 12 13 14 15 16 17 18 19 98 99
服务端代码
1 using System; 2 using System.IO; 3 using System.Linq; 4 using System.Web; 5 6 namespace UploadTest 7 { 8 public class UploadHelper 9 { 10 11 private UploadHelper() 12 { 13 14 } 15 16 public UploadHelper(string fileRootPath) 17 { 18 if (string.IsNullOrWhiteSpace(fileRootPath)) 19 throw new ArgumentNullException("fileRootPath", "fileRootPath is null"); 20 21 FileRootPath = fileRootPath; 22 BlockRootPath = fileRootPath + "/blocktmp/"; 23 } 24 ///25 /// 块文件存储根路径 26 /// 27 private string BlockRootPath { get; set; } 28 29 ///30 /// 文件存储根路径 31 /// 32 public string FileRootPath { get; set; } 33 34 ///35 /// 分块上传 36 /// 37 public UploadResult Upload(string uploadId, int blockCount, int currIndex, string fileName, HttpPostedFileBase file) 38 { 39 try 40 { 41 if (file == null) 42 return new UploadResult { Msg = "请选择文件~" }; 43 if (blockCount < 1) 44 return new UploadResult { Msg = "块数量不能小于1~" }; 45 if (currIndex < 0) 46 return new UploadResult { Msg = "块数量小于0~" }; 47 if (string.IsNullOrWhiteSpace(uploadId) && currIndex > 1) 48 return new UploadResult { Msg = "上传编号为空~" }; 49 50 var result = new UploadResult { Code = 1, Msg = "上传成功~" }; 51 52 //首次上传需创建上传编号 53 if (string.IsNullOrWhiteSpace(uploadId) || uploadId.Equals("undefind")) 54 uploadId = GenerateUploadId(); 55 56 result.UploadID = uploadId; 57 58 #region ==块处理== 59 60 //块文件名称 61 var blockName = $"{uploadId}_{currIndex}.block"; 62 //块文件目录路径 63 var blockPath = Path.Combine(BlockRootPath, uploadId); 64 //块文件目录对象 65 DirectoryInfo blockDirectoryInfo = Directory.Exists(blockPath) ? new DirectoryInfo(blockPath) : Directory.CreateDirectory(blockPath); 66 //块文件完整路径 67 var blockFullPath = Path.Combine(blockPath, blockName); 68 if (File.Exists(blockFullPath)) 69 { 70 //块已上传,不做失败处理 71 return new UploadResult { Code = 1, Msg = "该文件块已上传~" }; 72 } 73 74 file.SaveAs(blockFullPath); 75 76 #endregion 77 78 #region ==块合并处理== 79 80 //判断块文件是否已将上传完,上传完合并文件 81 if (blockDirectoryInfo.GetFiles().Count().Equals(blockCount)) 82 { 83 var timestamp = DateTime.Now.ToString("yyyMMdd"); 84 fileName = uploadId + "." + GetExtension(fileName); 85 var filePath = Path.Combine(FileRootPath, timestamp); 86 if (!Directory.Exists(filePath)) 87 { 88 Directory.CreateDirectory(filePath); 89 } 90 //完整文件存储路径 91 var fileFullPath = Path.Combine(filePath, fileName); 92 using (var fs = new FileStream(fileFullPath, FileMode.Create)) 93 { 94 for (var i = 1; i <= blockCount; i++) 95 { 96 var path = Path.Combine(blockPath, $"{uploadId}_{i}.block"); 97 var bytes = File.ReadAllBytes(path); 98 fs.Write(bytes, 0, bytes.Length); 99 }100 Directory.Delete(blockPath, true);101 102 result.FileInfo = new UploadFileInfo103 {104 FileName = fileName,105 FilePath = Path.Combine(timestamp, fileName)106 };107 }108 }109 110 return result;111 #endregion112 }113 catch (Exception ex)114 {115 return new UploadResult { Msg = ex.Message };116 }117 }118 119 ///120 /// 生成上传唯一编号121 /// 122 ///123 public string GenerateUploadId()124 {125 var guid = Guid.NewGuid().ToString();126 return guid.Replace("-", "");127 }128 129 /// 130 /// 获取文件扩展名131 /// 132 /// 133 ///134 public string GetExtension(string fileName)135 {136 if (string.IsNullOrWhiteSpace(fileName) || fileName.IndexOf(".") < 0)137 {138 return string.Empty;139 }140 var arr = fileName.Split('.');141 return arr[arr.Length - 1];142 }143 }144 /// 145 /// 文件上传结果146 /// 147 public class UploadResult148 {149 ///150 /// 状态码 0失败 1成功151 /// 152 public int Code { get; set; }153 ///154 /// 消息155 /// 156 public string Msg { get; set; }157 ///158 /// 上传编号,唯一159 /// 160 public string UploadID { get; set; }161 ///162 /// 文件保存信息163 /// 164 public UploadFileInfo FileInfo { get; set; }165 166 }167 public class UploadFileInfo168 {169 ///170 /// 文件保存名称171 /// 172 public string FileName { get; set; }173 ///174 /// 文件保存路径175 /// 176 public string FilePath { get; set; }177 ///178 /// 文件MD5值179 /// 180 public string MD5 { get; set; }181 }182 }
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[0]); return Json(result); } }}