您的位置:首页 > 编程语言 > C#

C#/.net批量大文件分块上传的实现

2016-04-05 14:30 302 查看
在写网站时,经常会用到文件上传这个。一般的图片上传什么的直接用fileupload倒也简单。但是遇到大文件上传就头大了。以前我也是使用别人的一个控件,但是总觉得不怎么好,也不是开源,就还是想自己写一个。

实现过程也还是挺简单的,但是仅支持html5,低版本的浏览器就不支持了。

这里用的是html5的formdata来获取文件,并用slice分割后,逐个上传。

JS:

function ReadFile(files) {
var len = files.length;
var per;
var uploadLength = new Array(len);
for (var file_index = 0; file_index < len; file_index++) {
$('.list' + file_index + ' .pro .probar').width(0);
var file = files[file_index];
var size = file.size;
var shardSize = 2097152;//以2M为一块,这个可以自定义
var shardCount = Math.ceil(size / shardSize);
var tryTimes = 0;
uploadLength[file_index] = 0;//已完成的文件长度
for (var bock_index = 0; bock_index < shardCount; bock_index++) {
if (tryTimes >= 3) {
//失败三次跳出循环避免死循环,显示失败信息
tryTimes = 0;
$('.list' + file_index + ' .pro .probar').addClass("displayN");
$('.list' + file_index + ' .pro').append("<div>上传失败</div>");
break;
}

var start = bock_index * shardSize;
var end = Math.min(size, start + shardSize);
var formdata = new FormData();
formdata.append("data", file.slice(start, end));
formdata.append("name", file.name);
formdata.append("total", shardCount);
formdata.append("index", bock_index);
formdata.append("file_index", file_index);
formdata.append("file_length", size);
formdata.append("shardSize", shardSize);
$.ajax({
url: "Upload",
type: "post",
data: formdata,
async: true,
processData: false,//不对form进行处理
contentType: false,//指定为false才能形成正确的Content-Type
success: function (data, textStatus) {
var findex = data.findex;
if (data.Error == 0) {
uploadLength[findex] += data.successlength;
var thislength = data.file_length;
per = Math.ceil(uploadLength[findex] / thislength * 100) + "%";
$('.list' + findex + ' .pro .probar').width(per);
if (per != '100%')
$('.list' + findex + ' .w100 .spprocess').text(per);
else {
$('.list' + findex + ' .w100 .spprocess').text('完成');
}
}
else if (data.Error == 1) {
//发生错误,重新上传
bock_index--;
tryTimes++;
} else {
tryTimes = 3;
$('.list' + findex + ' .pro .probar').addClass("displayN");
$('.list' + findex + ' .pro').append("<div>上传失败</div>");
}

},
error: function (XMLHttpRequest, textStatus, errorThrown) {
//发生错误,重新上传
bock_index--;
tryTimes++;
}
});
}
}
}
这里确实有几个需要注意的地方。

首选要明确这个是异步上传,它与同步上传的差别还是非常大的。认真看代码的可以发现,里面一些变量是传入后台,又从后台获取。这么做的一个目的,是保持变量的值。比如:

在for循环里的几个变量如file_index和bock_index,你在浏览器中断点ajax的success查看就会发现,它们并非是从0-n的依次变化,由于是异步上传,for并不会等待ajax的操作结束,ajax向后台提交后,for就继续往下走,往往等ajax收到后台返回的信息后,for循环已经结束,这里候在success里获取的bock_index或者file_index都是最后一个。只有提交后台后,再由后台发回来,才能够取到正确的信息。如果是同步上传,这些就是不必要的了。或者说我们不需要显示上传进度也不需要这么做。

error里面的意思是发生错误后重新上传,这个在同步上传中有用,但异步操作的话,我觉得也是没什么用的。可以删去。

.net:

public ActionResult Upload()
{
string name = Request.Form["name"];
int total = Int32.Parse(Request.Form["total"]);
int index = Int32.Parse(Request.Form["index"]);
System.Web.HttpPostedFileBase data = Request.Files["data"];

long file_length = long.Parse(Request.Form["file_length"]);
int shardSize = Int32.Parse(Request.Form["shardSize"]);
string dir = Server.MapPath("~/upload/");
string file_name = null;
FileStream fs = null;
int bock_length = 0;
try
{
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
//块文件的存放
//string bock_name = Path.Combine(dir, name + "_" + index);
//data.SaveAs(bock_name);
//实际文件的路径
file_name = Path.Combine(dir, name);
//写入文件中
if (!System.IO.File.Exists(file_name))
{
//构建与要上传文件大小相同的文件
fs = new FileStream(file_name, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
fs.SetLength(file_length);
fs.Close();
}
Stream st = data.InputStream;
<span style="white-space: pre;">		</span>long getLength = st.Length;
byte[] bytesBock = new byte[getLength];
st.Read(bytesBock, 0, (int)getLength);
fs = new FileStream(file_name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
int offset = index * shardSize;//写入文件偏移量
//bytesBock = System.IO.File.ReadAllBytes(bock_name);
bock_length = bytesBock.Length;
//System.IO.File.Delete(bock_name);
//写入字节流
fs.Seek(offset, SeekOrigin.Begin);//移动到要写入的位置
fs.Write(bytesBock, 0, bock_length);
fs.Close();
}
catch (Exception e)
{
fs.Close();
if (file_name != null)
WriteLog(file_name, index, "save" + e.ToString());
else WriteLog("noname", index, e.ToString());
return Json(new { Error = 1, findex = Request.Form["file_index"] });
}

return Json(new { Error = 0, findex = Request.Form["file_index"],index=index, successlength = bock_length, file_length = file_length });
}
这个类似于迅雷的下载,先创建一个与要上传的文件大小的文件,然后再往里面相应的位置写入数据。这里的关键是offset,这个偏量。

这里比较关键的还是Write(byte[] array, int offset, int count)这个方法,offset这个偏移量,我一开始时也搞成了要写入文件的偏移量,后来查了才知道,原来是待入的byte[]的偏移量。

一些不懂的方法可以去查查。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: