今天正式开始研究SunriseUpload.0.9.1的源码。
先看web.config里的内容:
<!-- Settings of SunriseUpload -->
<httpRuntime useFullyQualifiedRedirectUrl="true" maxRequestLength="100819200" executionTimeout="900" />
<httpModules>
<add name="HttpUploadModule" type="Sunrise.Web.Upload.HttpUploadModule, Sunrise.Web.Upload" />
</httpModules>
<httpHandlers>
<add verb="*" path="progress.ashx" type="Sunrise.Web.Upload.HttpUploadHandler, Sunrise.Web.Upload" />
</httpHandlers>
<!-- ========================= -->
第一个设定是上传文件的最大长度和执行时间。我不知道其它的组件里是怎样处理的,像没有这些设定。
关键的还是第二个,它表示所有的上传请求都将映射到Sunrise.Web.Upload.HttpUploadModule模块上,这样使得我们自己的上传请求都将失败。所以如果想自己
测试上传的时候,请注释掉这一设定。
第三个设定是对进度条的请求,由于ashx的扩展各被IIS映射在aspnet.dll上,所以可以通过httpHeadler将请求拦截,这一功能比httpMoudle小一些,它只对
progress.ashx的请求进行拦截,当然是里可以用通配符。
SingleUpload的示例:
很简单的一些设定:只有一个上传的input(type=file,注意它的value属性是只读的)
然后一个button,最后是一个表格显示上传后的数据。
protected Table tbFileList;
protected Button btnUpload;
private void btnUpload_Click(object sender, EventArgs e)
{
string path = Path.Combine(Server.MapPath("."), "UploadFile");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
//获取上传文件,"files"是<input type=file>的name属性
UploadFile uploadFile = UploadHelper.GetUploadFile("file1");
if(uploadFile != null)
{
TableRow tr = new TableRow();
TableCell[] tc = new TableCell[2] {new TableCell(), new TableCell()};
tc[0].Text = Path.GetFileName(uploadFile.FileName);
tc[1].Text = uploadFile.ContentLength.ToString("###,###") + " K bytes";
tr.Cells.AddRange(tc);
tbFileList.Rows.Add(tr);
//Save file
uploadFile.SaveAs(Path.Combine(path, Path.GetFileName(uploadFile.FileName)));
}
}
去掉细节,可以简化到:
private void btnUpload_Click(object sender, EventArgs e)
{
string path = Path.Combine(Server.MapPath("."), "UploadFile");
//获取上传文件,"files"是<input type=file>的name属性
UploadFile uploadFile = UploadHelper.GetUploadFile("file1");
if(uploadFile != null)
{
uploadFile.SaveAs(Path.Combine(path, Path.GetFileName(uploadFile.FileName)));
}
}
这里使用用了UploadHelper的静态成员函数,GetUpladFile,我觉得这不是一个好的做法,所以其它的组件里都改掉了这一做法。
下面我们就去看看UploadHelper类。
UploadHelper类有点长,近500行的代码。先看看核心函数:GetUploadFile()
public static UploadFile GetUploadFile(string name)
{
UploadFile uploadFile = new UploadFile(name);
return (uploadFile.FilePath == string.Empty) ? null : uploadFile;
}
呵呵,比想像的简单得多吧!!!接下来就又要看UploadFile类了!
UploadFile类代码不长,200多行,但都不好理解,要对HTTP协议有所了解。
先看看构造函数:
public UploadFile(string name)
{
if ((name == null) || (name == string.Empty))
{
throw new ArgumentNullException("Name", "Name can not be null!");
}
string content = String.Empty;
this.filename = string.Empty;
this.filepath = string.Empty;
this.contenttype = string.Empty;
this.filelength = 0;
前面的是初始化变量,直接从这里开始分析:看后面的解释
if (IsContentHeader(name))
{
content = name;
}
else if (IsContentHeader(Utils.GetContext().Request[name]))
{
content = Utils.GetContext().Request[name];
}
if ((content == null) || (content == string.Empty))
{
return;
}
//Get file info from content.
string[] contentArray = content.Split(';');
this.contenttype = contentArray[0];
this.contenttype = this.contenttype.Substring(14, (this.contenttype.Length - 14));
this.filename = contentArray[1];
this.filename = this.filename.Substring(10, (this.filename.Length - 11));
this.filepath = contentArray[2];
this.filepath = this.filepath.Substring(10, (this.filepath.Length - 11));
string uploadFolder = Utils.GetUploadFolder();
this.filepath = Path.Combine(uploadFolder, this.filepath);
try
{
this.filelength = new FileInfo(this.filepath).Length;
}
catch (Exception exception)
{
string uploadGuid = Utils.GetContext().Request["Sunrise_Web_Upload_UploadGUID"];
if (uploadGuid != null)
{
Utils.GetContext().Application.Remove(("_UploadGUID_" + uploadGuid));
}
throw exception;
}
}
//
研究说明:
if (IsContentHeader(name))
{
content = name;
}
else if (IsContentHeader(Utils.GetContext().Request[name]))
{
content = Utils.GetContext().Request[name];
}
这里又用name(上传文件的名字)参数调用了IsContentHeader函数,开始我不大明白,后面的分隔数组是怎么回事?上传的控件名不就是个字符串吗?实际上这
个函数在一次上传过程中被调用了两次,其中后面的分隔就是为了下次调用时做准备的,这是我的输出日志:
11/1/2005 4:58:04 PM file1
11/1/2005 4:58:04 PM Content-Type: video/x-ms-wmv;filename="D:\video\Defense.wmv";filepath="ec2f062d-21ab-4591-ada0-892a32a05625.wmv"
可以看到,第二次调用的时候,是以文件的上传信息来调用的,我们先不管它。往后看。
private bool IsContentHeader(string line)
{
if((line == null)||(line == String.Empty))
{
return false;
}
string[] contentArray = line.Split(';');
if (((contentArray.Length == 3)
&& contentArray[0].StartsWith("Content-Type:"))
&& (contentArray[1].StartsWith("filename=\"")
&& contentArray[2].StartsWith("filepath=\"")))
{
return true;
}
return false;
}
显然第一次调用是返回false;
第二次调用是在
else if (IsContentHeader(Utils.GetContext().Request[name]))
那么我们又要看看Utils类了。
public static HttpContext GetContext()
{
HttpContext context = HttpContext.Current;
if (context == null)
{
throw new Exception("HttpContext not found");
}
return context;
}
它返回当前HTTP请求的所有信息,里面就包含了文件上传的源信息。关于HttpContext类,可以查看MSDN里的帮助。也就是说,我们在HttpContext里取得数据的时
候,所得到的信息是完整的,所以我们可以得到上传文件的文件路径以文件类型。我们可以自己试一下:
1、建立一个新的Web Application,
2、创建一个类,
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;
using System.Xml;
namespace WebbTest
{
/// <summary>
/// Summary description for UploadTest.
/// </summary>
public class HttpModuleTest : IHttpModule
{
public HttpModuleTest()
{
//
// TODO: Add constructor logic here
//这里只是一个测试的调试输出,看看这个类为我们都做了些什么?
WebbSystem.TraceMsg("Construct the HttpModule.");
}
public void Dispose()
{
}
public void Init(System.Web.HttpApplication m_application)
{
}
}
}
3、添加到Web.config里,让所有的请求都要经过HttpModuleTest。
<httpModules>
<add name="WebbTest" type="WebbTest.HttpModuleTest, WebbTest"/>
</httpModules>
4、然后测试页面,可以得到结果:
11/1/2005 5:22:36 PM Construct the HttpModule.
11/2/2005 8:32:55 AM Construct the HttpModule.
(呵呵,我才发现一个问题,第二个时间上的信息是VS.net打开项目的时候产生的,因为VS.net在打开项目的时候对服务器做了一次请求。所以被记录下了。)
利用这个原理,我们是不是可以自己做一个网站统计系统呢?
好了,继续向下分析代码。
文章来源:http://computer.mblogger.cn/wucountry/posts/48478.aspx