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

Asp.Net 上传大文件专题(3)--从请求流中获取数据并保存为文件[上]

2010-04-27 11:33 585 查看
回顾上一篇,我们可以了解到以下内容:

HTTP请求流到达服务器后,由IIS进程或http.sys接收并调用ASP.NET ISAPI 扩展,接着生成HttpWorkerRequest并将HttpWorkerRequest传递给ProcessRequestInternal方法,这之后才创建了HttpContext请求上下文和HttpApplication 类的实例,然后又经过一系列处理并最终将消息返回(也就是送回客户端浏览器)。

本篇概述:
在本篇中,主要是从HTTP请求流中将数据部分进行截取,同时将数据相关信息进行保存。通过本篇你可以实现多个大文件的上传功能(实验平台 XP SP2,IIS 5.1, VS 2005)。
注:因为当初只是为了实现这个功能,所以并没有太多的考虑过性能和断点续传的功能。不过针对这些内容,大概已经有了个构思,可能会在写完这个专题后找时间在目前所实现的功能之上再考虑进这些元素,然后对这个专题进行相应的补充。

正文部分:

本篇中我们要做的就是在尽可能早的事件中对这个请求进行修改,截取其中上传的数据部分,然后重新将请求进行封装。这样就不会因为上传文件太大等原因引发异常。Asp.NET提供了HttpModule(HTTP模块),在模块的 Init 方法内,可以订阅各种应用程序事件(如 BeginRequestEndRequest),而HttpApplication.BeginRequest 事件始终是请求处理期间发生的第一个事件。为了让大家更多的理解模块,我引用了一段MSDN上关于HTTP模块的说明:

<httpModules>
<add name="modulename" type="classname,assemblyname" />
</httpModules>

[/b]
根据上面的格式,我写的如下:
<httpModules>
<add name="MyHttpModule1" type="MyHttpModule" />
</httpModules>

由于后期测试8M左右基本够用,因些设置maxRequestLength="8192"。

2、创建一个与type的值相同的类MyHttpModule,注意这个类必须实现 IHttpModule 接口,这样才能实现HttpModule的基本功能。
当我们实现IHttpModule接口后,在"I"的下方会出现短横,我们只需要将鼠标移上去便会出现可用的选项,你只需要傻瓜式的点击,系统便自动替我们往类中添加了模块的初始化事件和处置事件,当然有的朋友喜欢完全自己写也可以。代码如下:

public void Dispose()
{
throw new Exception("The method or operation is not implemented.");
}

public void Init(HttpApplication context)
{
throw new Exception("The method or operation is not implemented.");
}

3、去掉原Init中的事件,添加我们自己的事件
总算到了关键的步骤了,我们就是要在这里实现我们截取HTTP请求,获取文件信息,获取上传进度等事件。

3.1 判断是否是上传文件
因为需要上传文件的FORM表单都必须设置FORM表彰中的enctype属性为multipart/form-data(如果我们使用FileUpload组件,则会在编译时自动替我们加上这个属性)。所以我们只需要判断HTTP请求头的ContentType值是否为multipart/form-data即可,如果不是,我们就没必要对该请求进行处理了。
enctype="multipart/form-data实际上是一种编码规范(默认的话是application/x-www-form-urlencoded,该方式不适合大块二进制数据传输),该编码方式的基本思想就是用分隔符来分隔数据项,分隔符如“-----------------------------7d87d1cc0a88”。

3.2 获取HTTP请求总长度和HttpWorkerRequest对象
通过获取HttpApplication.Request.ContentLength属性便可获取HTTP请求内容的总长度,以便我们后来的使用。
HttpWorkerRequest对象在第二篇中有所提到,如果朋友们有看过,那应该可以认识到它其实内含了HTTP请求的全部内容,除此之外,还包括一些对请求进行处理的方法。
接下来我们就要使用这个对象对请求进行处理。
以下代码用来获取该对象(摘自网上):
HttpWorkerRequest GetWorkerRequest(HttpContext context)
{

IServiceProvider provider = (IServiceProvider)HttpContext.Current;
return (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
}

3.3 判断是否已经预先加载了HTTP请求的部分数据
因为包含上传文件的HTTP请求含有大量的数据,所以客户端并非一次性将整个请求全部发送到网上,而是分多次发送,大家可以通过协议分析软件进行观察。因此服务器的侦听端可能会预先侦听到已经发送到服务器端的部分数据。我们可以使用HttpWorkerRequest.GetPreloadedEntityBody()方法获取HTTP请求上下文已经被读取的部分,[这里有一个非常头痛的问题,我几乎就快因为这个问题放弃继续实现这个功能了。问题就是在.net 调试情况下,GetPreloadedEntityBody()时常取不到值,后在网上发现很多类似问题,有朋友说明是因为在IIS中使用ISAPIWorkerRequest , 而.net自带的web服务器则使用SimpleWorkerRequest。因此换成IIS调试,几乎100%可以收到数据。但是这样断点什么的便无法使用,郁闷

。现在根据第二篇提到的信息分析下,可能原因是ISAPIRuntime的ProcessRequest方法未能每次都将SimpleWorkerRequest转换成合适的HttpWorkRequest,于是造成数据获取失败]
如果没有预先加载,可以使用ReadEntityBody方法直接从异名管道中提取HTTP请求数据,[注:如果在.net 调试情况下无法获取预先加载的数据,那99%使用这个办法同样无法获取] ,由于我在使用IIS进行调试N次也没有发生过读取不到预先加载的数据的现象,所以基本不需要另外使用此方法来获取第一次数据。
如果以上方法还是无法读取到HTTP请求的第一次数据,那么基本确定此次上传请求失败。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐