大容量文件上传早已不是什么新鲜问题,在.net 2.0时代,HTML5也还没有问世,要实现这样的功能,要么是改web.config,要么是用flash,要么是用一些第三方控件,然而这些解决问题的方法要么很麻烦,比如改配置,要么不稳定,比如文件上G以后,上传要么死掉,要么卡住,通过设置web.config并不能很好的解决这些问题。
这是一个Html5统治浏览器的时代,在这个新的时代,这种问题已被简化并解决,我们可以利用Html5分片上传的技术,那么Plupload则是一个对此技术进行封装的前端脚本库,这个库的好处是可以自动检测浏览器是否支持html5技术,不支持再检测是否支持flash技术,甚至是sliverlight技术,如果支持,就使用检测到的技术。
那么这个库到哪里下载,怎么搭建呢,比较懒的童鞋还是用Install-Package Plupload搞定吧,一个命令搞定所有事
下面给出一个例子,使用自已定义的控件来使用Plupload (Plupload也有自己的界面可以用),如下
Plupload支持的功能这里就不细说了,什么批量上传,这里我没有用到,主要是感觉它支持的事件非常丰富,文件选取后的事件,文件上传中的事件(可获得文件的上传进度),文件上传成功的事件,文件上传失败的事件,等等
我的例子主要是上传一个单个文件,并显示上传的进度条(使用jQuery的一个进度条插件)
下面的例子主要是为文件上传交给 UploadCoursePackage.ashx 来处理
上传对象
var uploader = new plupload.Uploader({
browse_button: 'browse',//触发文件选择对话框的按钮,为那个元素id
runtimes: 'html5,flash,silverlight,html4',//兼容的上传方式
url: "Handlers/UploadCoursePackage.ashx",//后端交互处理地址
max_retries: 3,//允许重试次数
chunk_size: '10mb',//分块大小
rename: true,//重命名
dragdrop: false,//允许拖拽文件进行上传
unique_names: true,//文件名称唯一性
filters: { //过滤器
max_file_size: '999999999mb',//<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>最大尺寸
mime_types: [ //允许<a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>的<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>类型
{ title: "Zip",extensions: "zip" },{ title: "PE",extensions: "pe" }
]
},//<a href="/tag/zidingyi/" target="_blank" class="keywords">自定义</a>参数 (键值对形式) 此处可以定义参数
multipart_params: {
type: "misoft"
},// FLASH的配置
flash_swf_url: "../Scripts/plupload/Moxie.swf",// Silverligh的配置
silverlight_xap_url: "../Scripts/plupload/Moxie.xap",multi_selection: false //true:ctrl多<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a><a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>,false 单<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a><a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>
});
//在实例对象上<a href="/tag/diaoyong/" target="_blank" class="keywords">调用</a>init()<a href="/tag/fangfa/" target="_blank" class="keywords">方法</a>进行初始化
uploader.init();
uploader.bind('FilesAdded',function (uploader,files) {
$("#<%=fileSource.ClientID %>").val(files[0].name);
$.ajax(
{
type: 'post',url: 'HardDiskSpace.aspx/GetHardDiskFreeSpace',data: {},dataType: 'json',contentType: 'application/json;charset=utf-8',success: function (result) {
//选择<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>以后检测服务器剩余磁盘空间是否够用
if (files.length > 0) {
if (parseInt(files[0].size) > parseInt(result.d)) {
$('#error-msg').text("<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>容量大于剩余磁盘空间,请联系<a href="/tag/guanliyuan/" target="_blank" class="keywords">管理员</a>!");
} else {
$('#error-msg').text("");
}
}
},error: function(xhr,err,obj) {
$('#error-msg').text("检测服务器剩余磁盘空间失败");
}
});
});
uploader.bind('UploadProgress',file) {
var percent = file.percent;
progressBar.progress(percent);
});
uploader.bind('FileUploaded',function (up,file,callBack) {
var data = $.parseJSON(callBack.response);
if (data.statusCode === "1") {
$("#<%=hfPackagePath.ClientID %>").val(data.filePath);
var id = $("#<%=hfCourseID.ClientID %>").val();
__doPostBack("save",id);
} else {
hideLoading();
$('#error-msg').text(data.message);
}
});
uploader.bind('Error',err) {
alert("<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a><a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>失败,<a href="/tag/cuowu/" target="_blank" class="keywords">错误</a>信息: " + err.message);
});
后台 UploadCoursePackage.ashx 的代码也重要,主要是文件分片跟不分片的处理方式不一样
namespace WebUI.Handlers
{
///
/// UploadCoursePackage 的摘要说明
///
public class UploadCoursePackage : IHttpHandler
{
{
///
/// UploadCoursePackage 的摘要说明
///
public class UploadCoursePackage : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
int statuscode = 1;
string message = string.Empty;
string filepath = string.Empty;
if (context.Request.Files.Count > 0)
{
try
{
string resourceDirectoryName = System.Configuration.ConfigurationManager.AppSettings["resourceDirectory"];
string path = context.Server.MapPath("~/" + resourceDirectoryName);
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
int chunk = context.Request.Params["chunk"] != null ? int.Parse(context.Request.Params["chunk"]) : 0; //<a href="/tag/huoqu/" target="_blank" class="keywords">获取</a>当前的块ID,如果不是分块<a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>的。chunk则为0
string fileName = context.Request.Params["name"]; //这里写的比较潦草。判断<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>名是否为空。
string type = context.Request.Params["type"]; //在前面JS中不是定义了<a href="/tag/zidingyi/" target="_blank" class="keywords">自定义</a>参数multipart_params的值么。其中有个值是type:"misoft",此处就可以<a href="/tag/huoqu/" target="_blank" class="keywords">获取</a>到这个值了。<a href="/tag/huoqu/" target="_blank" class="keywords">获取</a>到的type="misoft";
string ext = Path.GetExtension(fileName);
//fileName = string.Format("{0}{1}",Guid.NewGuid().ToString(),ext);
filepath = resourceDirectoryName + "/" + fileName;
fileName = Path.Combine(path,fileName);
//对<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>流进行存储 需要注意的是 files目录必须存在(此处可以做个判断) 根据上面的chunk来判断是块<a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>还是普通<a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a> <a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>方式不一样 ,导致的保存方式也会不一样
FileStream fs = new FileStream(fileName,chunk == 0 ? FileMode.OpenOrCreate : FileMode.Append);
//write our input stream to a buffer
Byte[] buffer = null;
if (context.Request.ContentType == "application/octet-stream" && context.Request.ContentLength > 0)
{
buffer = new Byte[context.Request.InputStream.Length];
context.Request.InputStream.Read(buffer,buffer.Length);
}
else if (context.Request.ContentType.Contains("multipart/form-data") && context.Request.Files.Count > 0 && context.Request.Files[0].ContentLength > 0)
{
buffer = new Byte[context.Request.Files[0].InputStream.Length];
context.Request.Files[0].InputStream.Read(buffer,buffer.Length);
}
//write the buffer to a file.
if (buffer != null)
fs.Write(buffer,buffer.Length);
fs.Close();
statuscode = 1;
message = "<a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>成功";
}
catch (Exception ex)
{
statuscode = -1001;
message = "保存时发生<a href="/tag/cuowu/" target="_blank" class="keywords">错误</a>,请确保<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>有效且格式正确";
Util.LogHelper logger = new Util.LogHelper();
string path = context.Server.MapPath("~/Logs");
logger.WriteLog(ex.Message,path);
}
}
else
{
statuscode = -<a href="/tag/404/" target="_blank" class="keywords">404</a>;
message = "<a href="/tag/shangchuan/" target="_blank" class="keywords">上传</a>失败,未接收到资源<a href="/tag/wenjian/" target="_blank" class="keywords">文件</a>";
}
string msg = "{\"statusCode\":\"" + statuscode + "\",\"message\":\"" + message + "\",\"filePath\":\"" + filepath + "\"}";
context.Response.Write(msg);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
再附送一个检测服务器端硬盘剩余空间的功能吧
namespace WebUI
{
public partial class CheckHardDiskFreeSpace : System.Web.UI.Page
{
protected void Page_Load(object sender,EventArgs e)
{
{
public partial class CheckHardDiskFreeSpace : System.Web.UI.Page
{
protected void Page_Load(object sender,EventArgs e)
{
}
/// <summary>
/// <a href="/tag/huoqu/" target="_blank" class="keywords">获取</a>磁盘剩余容量
/// </summary>
/// <returns></returns>
[WebMethod]
public static string GetHardDiskFreeSpace()
{
const string strHardDiskName = @"F:\";
var freeSpace = string.Empty;
var drives = DriveInfo.GetDrives();
var myDrive = (from drive in drives
where drive.Name == strHardDiskName
select drive).FirstOrDefault();
if (myDrive != null)
{
freeSpace = myDrive.TotalFreeSpace+"";
}
return freeSpace;
}
}
}