众所皆知,web上传大文件,一直是一个痛。上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的。
本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路。下面贴出简易DEMO源码分享:
前端页面:
@{
ViewBag.Title = "Upload";
}
<h2>Upload</h2><table class="table table-striped">
<tr>
<td><input type="file" id="file" onchange="selfile()" /></td>
<td><input type="button" value="上传" onclick="uploading()"/></td>
</tr>
<tr>
<td colspan="2">文件信息:<span id="fileMsg"></span></td>
</tr>
<tr>
<td colspan="2">当前进度:<span id="upsize"></span></td>
</tr></table><script src="~/Scripts/myUploader.js"></script><script type="text/javascript">
//guid
var guid = "@Guid.NewGuid()";
var uploader;
function selfile() {
var f = $("#file")[0].files[0];
uploader = new SupperUploader("@Url.Action("RecvUpload")", f, guid, (1024*1024));
$("#fileMsg").text("文件名:" + uploader.fileName + "文件类型:" + uploader.fileType + "文件大小:" + uploader.fileSize + "字节");
}
function uploading() {
uploader.UploadFun(function () {
$("#upsize").text(uploader.upedSize);
})
}</script>
SupperUploader是我自己封装的JS插件,源码如下:
var SupperUploader = function (uploadUrl, file, guid, cutSize) {
this.file = file;
//文件大小
this.fileSize = file.size;
//文件类型
this.fileType = file.type;
//文件路径
this.fileName = file.name;
//guid
this.guid = guid;
//分片大小
this.cutSize = cutSize,
//已上传
this.upedSize = 0;
//开始位置
this.startIndex = 0;
//结束位置
this.endIndex = 0;
//序号
this.indexr = 0;
//上传路径
this.uploadUrl = uploadUrl;
//合并结果
this.merged = false;
};
SupperUploader.prototype = {
UploadFun: function (uploadCallBack) {
if (this.merged)
return;
var thisobj = this;
$.ajax({
type: "POST",
url: thisobj.uploadUrl,
enctype: 'multipart/form-data',
data: thisobj.CutFileFun(),
processData: false,
contentType: false,
success: function (res) {
if (res == "success") {
if (thisobj.upedSize == thisobj.fileSize) {
thisobj.merged = true;
alert("已成功上传!")
return;
}
thisobj.upedSize += thisobj.cutSize;
if (thisobj.upedSize > thisobj.fileSize)
thisobj.upedSize = thisobj.fileSize;
thisobj.indexr+=1;
//执行回调函数 uploadCallBack();
//继续调用上传 thisobj.UploadFun(uploadCallBack);
}
}
});
},
CutFileFun: function () {
var formData = null;
if (this.upedSize < this.fileSize) {
this.startIndex = this.upedSize;
this.endIndex = this.startIndex + this.cutSize;
if (this.endIndex > this.fileSize) {
this.endIndex = this.fileSize;
}
var currentData = this.file.slice(this.startIndex, this.endIndex);
formData = new FormData();
formData.append("file", currentData);
formData.append("index", this.indexr);
formData.append("fname", this.fileName);
formData.append("guid", this.guid);
formData.append("ismerge", this.fileSize == this.endIndex);
}
return formData;
}
};
后端代码,此Demo是基于MVC架构的:
[HttpGet]
public ActionResult Upload() {
return View();
}
[HttpPost]
public ActionResult RecvUpload(){
try
{
string fileName = Request["fname"];
string index = Request["index"];
string guid = Request["guid"];
var file = Request.Files[0];
var ismerge = Request["ismerge"];
string tempDirpath = "~/Content/temp/" + guid + "/";
string savepath = tempDirpath + index + "_" + fileName;
//合并文件
if (bool.Parse(ismerge))
{
//获取所有分割文件
var files = System.IO.Directory.GetFiles(Server.MapPath(tempDirpath));
//文件FILEINFO
var infos = files.Select(x => new FileInfo(x)).ToList().OrderBy(x=>x.LastWriteTime).ToList();
//合并文件流
FileStream mergefs = new FileStream(Server.MapPath("~/Content/temp/" + fileName),FileMode.Append);
BinaryWriter bw = new BinaryWriter(mergefs);
FileStream tempfs = null;
BinaryReader tempbr= null;
infos.ToList().ForEach(f =>
{
tempfs = new FileStream(f.FullName, FileMode.Open);
tempbr = new BinaryReader(tempfs);
bw.Write(tempbr.ReadBytes((int)tempfs.Length));
tempfs.Close();
tempbr.Close();
});
bw.Close();
mergefs.Close();
//删除分块文件
infos.ForEach(f =>{
System.IO.File.Delete(f.FullName);
});
return Json("success");
}
if (!System.IO.Directory.Exists(Server.MapPath(tempDirpath))){
System.IO.Directory.CreateDirectory(Server.MapPath(tempDirpath));
}
using (FileStream fs = new FileStream(Server.MapPath(savepath), FileMode.CreateNew))
{
using (Stream stream = file.InputStream)
{
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
fs.Write(buffer, 0, buffer.Length);
}
}
return Json("success");
}
catch (Exception e)
{
return Json(e.Message);
}
}
在此分享!希望多多指正~
后端代码逻辑大部分是相同的,目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/07/java超大文件上传与下载/