C#/.net批量大文件分块上传的实现
2016-04-05 14:30
302 查看
在写网站时,经常会用到文件上传这个。一般的图片上传什么的直接用fileupload倒也简单。但是遇到大文件上传就头大了。以前我也是使用别人的一个控件,但是总觉得不怎么好,也不是开源,就还是想自己写一个。
实现过程也还是挺简单的,但是仅支持html5,低版本的浏览器就不支持了。
这里用的是html5的formdata来获取文件,并用slice分割后,逐个上传。
JS:
首选要明确这个是异步上传,它与同步上传的差别还是非常大的。认真看代码的可以发现,里面一些变量是传入后台,又从后台获取。这么做的一个目的,是保持变量的值。比如:
在for循环里的几个变量如file_index和bock_index,你在浏览器中断点ajax的success查看就会发现,它们并非是从0-n的依次变化,由于是异步上传,for并不会等待ajax的操作结束,ajax向后台提交后,for就继续往下走,往往等ajax收到后台返回的信息后,for循环已经结束,这里候在success里获取的bock_index或者file_index都是最后一个。只有提交后台后,再由后台发回来,才能够取到正确的信息。如果是同步上传,这些就是不必要的了。或者说我们不需要显示上传进度也不需要这么做。
error里面的意思是发生错误后重新上传,这个在同步上传中有用,但异步操作的话,我觉得也是没什么用的。可以删去。
.net:
这里比较关键的还是Write(byte[] array, int offset, int count)这个方法,offset这个偏移量,我一开始时也搞成了要写入文件的偏移量,后来查了才知道,原来是待入的byte[]的偏移量。
一些不懂的方法可以去查查。
实现过程也还是挺简单的,但是仅支持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[]的偏移量。
一些不懂的方法可以去查查。
相关文章推荐
- C#基础杂杂
- c# winform 获取listview 选中行某列的值
- C#使用SOAP获取webservice实例解析
- C# out和ref参数修饰符
- 对"使用Mono Runtime Bundle制作安装包让C#桌面应用程序脱离net framework"增加说明
- c# get set 说明
- mono嵌入式应用研究(三):注册内部函数
- 算法實例-C#-信箱排序-PigeonHoleSort
- C#获取单元格值(使用NPOI插件)
- c#处理基于Modbus协议的串口通讯程序
- C# Regex ignoring non-capturing group
- mono嵌入式应用研究(二):虚拟机的初始化
- c# log组件 log4net使用介绍
- C# 利用ICSharpCode.SharpZipLib实现在线压缩和解压缩
- leetcode 3 : Longest Substring Without Repeating Characters 最长无重复子串 (C# 语言版)
- C# The process cannot access the file because it is being used by another process
- 基于C# 生成Zip压缩包代码
- C# 操作INI配置文件
- C# Distinct使用,支持对象的相等比较
- vs2015中 c#如何 编译成低版本供xp使用