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. 使用EF连接Postgresql

    环境: VS2017 Community Windows 10 Postgresql 9.6 安装Postgresql: https://www.postgresql.org/download/ 1. ...

  2. 使用Github+Hexo框架搭建部署自己的博客

    前言 Hexo 是一个快速.简洁且高效的博客框架.Hexo 使用 Markdown (或其他渲染引擎 )解析文章, 在几秒内,即可利用靓丽的主题生成静态网页. 安装 安装前提 安装 Hexo 相当简单 ...

  3. js关闭当前窗口,window.close()方法只能是window.open打开的才能执行关闭

    js关闭当前窗口,window.close()方法只能是window.open打开的才能执行关闭. function closeWin() { //open(location, '_self').cl ...

  4. js将时间戳转成格式化的时间

    function getLocalTime(nS){ return new Date(parseInt(nS) * 1000).toLocaleString().replace(/年|月/g, &qu ...

  5. Android使用OKHTTP解析JSON数据

    为了代码重用,我们首先封装一个类.这个类是HttpUtil HttpUtil.java package com.example.asus.networktest; import okhttp3.OkH ...

  6. 5.如何修改maven本地仓库

    首先测试机子上时候安装上maven,步骤是win+r-->cmd-->mvn -v-->看其是否出现如下字样:   如果时间长了你忘记了你安装的maven目录或者jdk目录,那么下面 ...

  7. eclipse 导入git库 Android工程

    1. 导入git库 1.1 从git库 clone 代码 在file->import中选中Git 目录下的Projects from Git 点击Next 选择 URL 点击Next 输入URL ...

  8. SQL显示某月全部日期明细以及SQL日期格式

    SQL显示某月全部日期明细<存储过程> 方法一: declare @date datetime declare @end datetime ,getdate()) ,@date) crea ...

  9. [图形学] 习题8.12 NLN二维线段裁剪算法实现

    Nicholl-Lee-Nicholl二维线段裁剪算法相对于Cohen-Sutherland和Liang-Barsky算法来说,在求交点之前进行了线段端点相对于几个区域的判断,可以确切的知道要求交点的 ...

  10. 初学c语言

    虽然有一点点基础,但是还是从头学吧,这一周也就一些c语言的几个代码代表的意思和一个Hello world的程序. #include是头文件名,<>这是要返回的函数类型,然后是main主函数 ...