开发工具与关键技术:MVC、jQuery
作者:张俊辉
撰写时间:2019年03月01日
我为何要做分段上传这个功能呢?说白了就是搞事情!我们都知道,MVC不允许上传过大的文件,虽然可以通过修改配置文件提高上传大小,别人我不知道,而我或许是修改不当,偶尔能上传成功,但都以失败居多,所以上网找了很多资料,有许多其他框架上传的按例,却没发现有MVC的,但我从中学会了一些原理,因此写了这些代码,代码不是很成熟,希望对你们有帮助!
首先搭建好前端页面,在这里我就不做复杂的用了最简单最原始的文件类型的input标签,并且引用了jQuery插件,代码与效果图如下:
<div>
选择文件:<input type="file" id="file1" multiple accept="*" /><br />
</div>
<script src="~/Content/jquery-2.1.4.min.js"></script>
搭建好页面后开始书写javaScript代码,下方则是书写好的代码与注释:
//当上传文件input标签发生改变时触发事件
$("#file1").change(function () {
//每次上传的大小(下列相当于2M)
var cutSize = 2 * 1024 * 1024;
//获取文件
var file = $("#file1").prop('files')[0];
//当文件大小小于每次上传大小时
if (file.size < cutSize) {
//输出文件大小
console.log(file.size);
} else {
//否则向上去整数(意思就是分为多少段)
var fileLength = file.size / cutSize | 1;
//循环依次上传
for (var i = 0; i < fileLength ; i++) {
//声明一个对象以储存需要提交的字段与文件
var formData = new FormData();
//向formData添加一个字段count(总段数)
formData.append("count", fileLength)
//给每段文件重命名,后缀为(.part'i'i为数字)每段字段名字后缀都不一样,以i区分
formData.append("name", file.name + ".part" + i)
//下面开始截断文件,由于最后一次截断有少于2M的可能,所以当最后一次截断的位置大于文件总长度时,截断位置便等于文件总长度
if (cutSize * (i + 1) > file.size) {
//文件的最后一次截断
formData.append("file", file.slice(cutSize * i, file.size))
} else {
//由于i从0开始循环,因此文件也从0开始每节4M的长度截断,直至倒数第二节
formData.append("file", file.slice(cutSize * i, cutSize * (i + 1)))
}
//使用ajax异步提交方式提交字段与文件
ajaxFile(formData, fileLength);
};
}
});
var ss = 0;//记录当前提交到第几段
function ajaxFile(formData, count) {
$.ajax({
type: "post",
url: "/HuoQuShiJian/PostbigFile1",
data: formData,
processData: false,
contentType: false,
success: function (data) {
//每提交成功一次ss则加多1
ss = ss + data;
//输出进度百分比,当前提交段数/总段数*100
console.log((ss / count) * 100 + '%');
}
});
}
最后便是后台C#代码与注释了:
public ActionResult PostbigFile1()
{
//获取该段文件的文件名
var name = Request.Form["name"];
//获取当前文件的文件流
var file = Request.Files["file"];
//获取上传文件的 总段数
var count = Request.Form["count"];
//截断文件自定义的后缀名,获取原文件名
string newFileName = name.Substring(0, name.LastIndexOf('.'));
//判断指定目录是否存在临时存储文件夹,没有就创建(文件夹名以原文件名命名)
if (!System.IO.Directory.Exists(Server.MapPath("~/Document/Title/Temp/" + newFileName)))
{
//不存在就创建目录
System.IO.Directory.CreateDirectory(Server.MapPath("~/Document/Title/Temp/" + newFileName));
}
//创建合并后的文件储存路径
if (!System.IO.Directory.Exists(Server.MapPath("~/Document/Title/File/")))
{
//不存在就创建目录
System.IO.Directory.CreateDirectory(Server.MapPath("~/Document/Title/File/"));
}
//存储文件(临时文件夹)
file.SaveAs(Server.MapPath("~/Document/Title/Temp/" + newFileName + "\\" + name));
//获取临时存储目录下的所有切割文件
string[] allFile = Directory.GetFiles(Server.MapPath("~/Document/Title/Temp/" + newFileName));
//当获取文件的总条数等于上传文件的总条数时合并文件
if(allFile.Length== int.Parse(count))
{
//将文件进行排序拼接
allFile = allFile.OrderBy(s => int.Parse(Regex.Match(s, @"\d+$").Value)).ToArray();
//判断组合的文件是否存在(临时文件夹)
if (System.IO.File.Exists(Server.MapPath("~/Document/Title/File/" + newFileName)))//如果文件存在
{
System.IO.File.Delete(Server.MapPath("~/Document/Title/File/" + newFileName));//先删除,否则新文件就不能创建
}
//创建空的文件流(创建二进制写入)
FileStream FileOut = new FileStream(Server.MapPath("~/Document/Title/File/" + newFileName), FileMode.Create, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(FileOut);
//将截断上传后的文件循环写入文件流
for (int i = 0; i < allFile.Length; i++)
{
//输入流
FileStream FileIn = new FileStream(allFile[i], FileMode.Open);
BinaryReader br = new BinaryReader(FileIn);
//二进制写入参数
byte[] data = new byte[4194304]; //流读取,缓存空间
int readLen = br.Read(data, 0, data.Length);//每次实际读取的字节大小
//二进制写入
bw.Write(data, 0, readLen);
//关闭输入流
FileIn.Close();
br.Close();
}
//关闭二进制写入
bw.Close();
FileOut.Close();
return Json(1, JsonRequestBehavior.AllowGet);
}
return Json(1, JsonRequestBehavior.AllowGet);
}
效果图如下,上传页面展示:
临时文件图展示:
上传后的文件图展示: