在ASP.Net中,大文件上传一直是个棘手的问题。在WEB.Config文件中有对上传文件大小的限制。对于稍大点的文件可以适当更改配置,但我们不能无限制的括大它,这是由ASP.Net的上传机制所决定的,因为.Net是把文件全部加载到内存中才可以进行操作的(MSDN上说,对于过大的文件实际上也还是要放在硬盘上缓冲的)。对于此问题,最简单的解决办法是使用别人开发的组件,如:
例如,若想将上传文件的上限提高至20MB,我们只需要这样修改:
view plaincopy to clipboardprint?
<system.web>
</system.web>
<system.web>
</system.web>
FileUploader.NET (MediaChase公司,$310以上)
RadUpload (Telerik公司,$249)
NeatUpload (免费,遵守LGPL协议)
但有时候,我们并不想使用组件,那还有一个解决办法,在服务器端截获上传事件,并把数据分块上传。代码如下:
1、Config如下配置
<httpModules>
<add name="upFile" type="HttpUploadModule"/>
</httpModules>
<httpRuntime executionTimeout="240" maxRequestLength="10240" requestValidationMode="2.0" />
2、如下实现接口IHttpModule
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web;
using System.Reflection;
//实现IHttpModule接口
public class HttpUploadModule : IHttpModule
{
///<summary>
///默认无参构造函数
///</summary>
public HttpUploadModule()
{
}
///<summary>
///初始化,在此添加事件的响应
///</summary>
///<param name="application">Web应用程序对像</param>
public void Init(HttpApplication application)
{
//订阅响应HTTP请求事件
application.BeginRequest += new EventHandler(this.Application_BeginRequest);
}
///<summary>
///释放使用的资源
///</summary>
public void Dispose()
{
//
//TODO:如果需要手工为类做一些清除工作,在此添加代码
//
}
///<summary>
///开始一个HTTP请求事件的处理
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
private void Application_BeginRequest(Object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication; //获得Web应用程序对像
HttpWorkerRequest request = GetWorkerRequest(app.Context); //获得Http请求对像
Encoding encoding = app.Context.Request.ContentEncoding; //得到请求文本的编码类型
int bytesRead = 0; //已读数据大小
int read; //当前读取的块的大小
int count = 8192; //分块大小
byte[] buffer; //保存所有上传的数据
if (request != null) //如果请求不为空
{
//返回 HTTP 请求正文已被读取的部分。
byte[] tempBuff = request.GetPreloadedEntityBody();
//如果是附件上传
if (tempBuff != null && IsUploadRequest(app.Request))
{
//获取上传大小
long length = long.Parse(request.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength));
buffer = new byte[length]; //定义相应大小的缓存区
count = tempBuff.Length; //分块大小
//将已上传数据复制过去
Buffer.BlockCopy(tempBuff, //源数据
0, //从0开始读
buffer, //目标容器
bytesRead, //指定存储的开始位置
count); //要复制的字节数。
//开始记录已上传大小
bytesRead = tempBuff.Length;
//循环分块读取,直到所有数据读取结束
while (request.IsClientConnected() && !request.IsEntireEntityBodyIsPreloaded() && bytesRead < length)
{
//如果最后一块大小小于分块大小,则重新分块
if (bytesRead + count > length)
{
count = (int)(length - bytesRead);
tempBuff = new byte[count];
}
//分块读取
read = request.ReadEntityBody(tempBuff, count);
//复制已读数据块
Buffer.BlockCopy(tempBuff, 0, buffer, bytesRead, read);
//记录已上传大小
bytesRead += read;
}
if (request.IsClientConnected() && !request.IsEntireEntityBodyIsPreloaded())
{
//传入已上传完的数据
InjectTextParts(request, buffer);
}
}
}
}
///<summary>
///获得Http请求对像
///</summary>
///<param name="context">Http请求内容</param>
///<returns>Http请求对像</returns>
HttpWorkerRequest GetWorkerRequest(HttpContext context)
{
IServiceProvider provider = (IServiceProvider)HttpContext.Current;
return (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
}
///<summary>
///传入已上传完的数据
///</summary>
///<param name="request">Http请求对像</param>
///<param name="textParts"></param>
void InjectTextParts(HttpWorkerRequest request, byte[] textParts)
{
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
Type type = request.GetType(); //获得请求的类型
while ((type != null) && (type.FullName != "System.Web.Hosting.ISAPIWorkerRequest"))
{
type = type.BaseType;
}
if (type != null)
{
type.GetField("_contentAvailLength", bindingFlags).SetValue(request, textParts.Length);
type.GetField("_contentTotalLength", bindingFlags).SetValue(request, textParts.Length);
type.GetField("_preloadedContent", bindingFlags).SetValue(request, textParts);
type.GetField("_preloadedContentRead", bindingFlags).SetValue(request, true);
}
}
/// <summary>
/// 比较两个字符串指定位置的指定长度是否相等
/// 忽略大小写及区域不同
/// </summary>
/// <param name="s1">要比较的第一个字符串</param>
/// <param name="s2">要比较的第二个字符串</param>
/// <returns>
/// 小于零 str1 中的子字符串小于 str2 中的子字符串。
/// 等于零子字符串相等,或者 length 为零。
/// 大于零 str1 中的子字符串大于 str2 中的子字符串。
/// </returns>
private static bool StringStartsWithAnotherIgnoreCase(string str1, string str2)
{
return (string.Compare(str1, 0, str2, 0, str2.Length,true, CultureInfo.InvariantCulture) == 0);
}
///<summary>
///是否为附件上传
///判断的根据是ContentType中有无multipart/form-data
///</summary>
///<param name="request"></param>
///<returns></returns>
bool IsUploadRequest(HttpRequest request)
{
return StringStartsWithAnotherIgnoreCase(request.ContentType, "multipart/form-data");
}
}