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对象实现大文件分块上传(断点上传)功能的更多相关文章

  1. php大文件分块上传断点续传demo

    前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...

  2. .Net之使用Jquery Ajax通过FormData对象异步提交图片文件到服务端保存并返回保存的图片路径

    前言: 首先对于图片上传而言,在我们的项目开发中可以说出现的频率是相当的高的.这篇文章中,我将要描述的是在我们.Net中如何使用Jquery Ajax通过FormData对象异步提交图片文件到后台保存 ...

  3. JS原生上传大文件显示进度条-php上传文件

    JS原生上传大文件显示进度条-php上传文件 在php.ini修改需要的大小: upload_max_filesize = 8M    post_max_size = 10M    memory_li ...

  4. 使用PHP和HTML5 FormData实现无刷新文件上传教程

    无刷新文件上传是一个常见而又有点复杂的问题,常见的解决方案是构造 iframe 方式实现. 在 HTML5 中提供了一个 FormData 对象 API,通过 FormData 可以方便地构造一个表单 ...

  5. 利用blob对象实现大文件分片上传

    首先说分片上传,我们在进行文件上传的时候,因为服务器的限制,会限制每一次上传到服务器的文件大小不会很大,这个时候我们就需要把一个需要上传的文件进行切割,然后分别进行上传到服务器. 假如需要做到这一步, ...

  6. js大文件分块上传断点续传demo

    文件夹上传:从前端到后端 文件上传是 Web 开发肯定会碰到的问题,而文件夹上传则更加难缠.网上关于文件夹上传的资料多集中在前端,缺少对于后端的关注,然后讲某个后端框架文件上传的文章又不会涉及文件夹. ...

  7. HTML5 FormData对象

    利用FormData对象,你可以使用一系列的键值对来模拟一个完整的表单,然后使用XMLHttpRequest发送这个"表单". 创建一个FormData对象 你可以先创建一个空的F ...

  8. java大文件分块上传断点续传demo

    第一点:Java代码实现文件上传 FormFile file = manform.getFile(); String newfileName = null; String newpathname =  ...

  9. springboot集成websocket实现大文件分块上传

    遇到一个上传文件的问题,老大说使用http太慢了,因为http包含大量的请求头,刚好项目本身又集成了websocket,想着就用websocket来做文件上传. 相关技术 springboot web ...

随机推荐

  1. 工程师倾情奉献-Win7 ISO 精简操作说明

    1.前提条件 a)本文档内容只适用于32bit win7 install ISO,其它OS不能保证兼容 b)示范文件为win7-ultimate-rtm-32-en-us-rdvd.iso 2.准备待 ...

  2. SQL Server2012数据库的备份和还原

    一.数据库的备份: 1.选择要备份的数据库“accountInfo”,点击鼠标右键 → 任务 → 备份 2.在打开的“备份数据库 —accountInfo”对话框中,先点击删除,然后点击“添加” 3. ...

  3. Python处理Excel文件

    因为工作需求,需要审核一部分query内容是否有效,query储存在Excel中,文本内容为页面的Title,而页面的URL以HyperLink的格式关联到每个Cell. 于是本能的想到用Python ...

  4. 一步一步教你将开源项目上传到jcenter

    最近闲着看了一下jcenter的使用,也想将自己使用频率比较高的东西抽成类库,然后通过compile来使用,提高开发效率,本来以为挺简单的,但是网上介绍的比较模糊,很多博客也比较老了,不适用,花了我一 ...

  5. 仿淘宝,京东红包雨(基于Phaser框架)

    本红包雨项目是基于HTML5的游戏框架Phaser写的,最终形成的是一个canvas,所以性能很好,但是必须要说的是这个框架比较大,压缩后也有700K左右,所以请慎用. 代码地址: https://g ...

  6. String,StringBuffer与StringBuilder

    1. String,StringBuffer与StringBuilder的区别 String:存储在常量池中:是不可变的字符序列,任何对String值的改变都会引发新的String对象的生成,因此执行 ...

  7. Ionic开发笔记

    Ionic 开发笔记 记录开发中遇到的一些问题 ion-side-menu,使所有顶部导航标题居中 <!-- 添加 align-title="center" 使顶部导航标题居 ...

  8. Python对象类型及其运算

    Python对象类型及其运算 基本要点: 程序中储存的所有数据都是对象(可变对象:值可以修改 不可变对象:值不可修改) 每个对象都有一个身份.一个类型.一个值 例: >>> a1 = ...

  9. OJ2236“孤单数”题目报告

    题目描述:有2n+1个数,其中有n对数字是成双出现的,有且仅有1个数字只有它自己一个.请你找出这个孤单数. 输入描述: 第一行有且只有一个正整数n(n<=500000) 第二行有2n+1个数ai ...

  10. 拥抱.NET Core系列:依赖注入(2)

    上一篇"拥抱.NET Core系列:依赖注入(1)"大体介绍了服务注册.获取和生命周期,这一篇来做一些补充. 由于内容跨度大(.NET Core.ASP.NET Core),所以文 ...