[导入]SunriseUpload.0.9.1的源码分析(一)

今天正式开始研究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
================================
  /\_/\                        
 (=^o^=)  Wu.Country@侠缘      
 (~)@(~)  一辈子,用心做一件事!
--------------------------------
  学而不思则罔,思而不学则怠!  
================================
原文地址:https://www.cnblogs.com/WuCountry/p/305670.html