[系列文章]上传文件管理控件之v1

上传文件管理控件v1:
总引言:
我是从asp转到asp.net上面来的。这次开发一个上传文件管理的控件,最开始的时候,我是想在asp.net里面把原来asp代码的功能重新实现一遍。结果中途出现了难题,我就打算往asp.net上逐渐迁移。所以这里面上传文件管理控件也就有了4个版本。
V1——类asp代码。
V2——asp.net 1.x 用的 DataGrid
V3——asp.net 2.0 用的GridView
V4——asp.net 2.0 ObjectDataSource + GridView
在我写这个文档的时候,这个4个版本的控件代码还混合在一起,功能上基本只有雏形。我准备四个版本的代码分离开来,重点把v1和v4的完整功能开发出来,v2和v3,暂时就不考虑了。
V1引言:
以前开发asp的时候,根据动网7.0里面模块,改出了一个后台上传文件管理的功能。现在刚刚开始用asp.net开发,不知道相同的功能,得怎么样才能实现。于是我决定先用asp时代的方法试一试吧。
大概的思路是,程序找到要管理的目录,然后获取目录里的所有文件,这时候得到一个文件数组,再将这个数组的所有成员全部罗列出来。删除的时候,向服务器提交要删除的文件名,服务器根据文件名删除文件。
开发的过程中遇到了一些情况,我把这些情况,和解决的过程记在下面。

一、用户控件
从asp+开始,我就接触asp.net了。但是一直以来由于懒惰和其它的原因,断断续续的一直在学,一直也在忘,所以这次,我要把上传文件管理做成一个控件,首先要做的就是翻书看看用户控件是怎么做的。很不幸的是,在asp.net 2.0里面,不怎么重视用户控件,手里的两部近千页的书中都说得不太细,只好结合着msdn来看了。
简而言之,就是在vs2005新建文件的时候,选择类型为“web用户控件",然后生成一个“.ascx”文件,然后在这个文件里面做一些动作,这就是用户控件。需要的时候,再把这个文件从“解决方案资源管理器”中拖到某个webform里就行了。我这里说得很简单(其实也就是这么简单,其它的东西都是正常asp.net技术),更多的内容,请参考msdn。

二、没有使用代码分离

在使用控件之前,我已经制作一个简单的webform,在里面实现了列举一个文件夹所有文件的功能。一开始,我只不过想做简单的尝试,并且我用的是asp的思路来实现,所以没有采用代码分离(也是没办法采用代码分离)。因为我手里的asp.net 2.0教材全是代码分离的,所以不采用代码分离,反而遇到了一些额外的问题:
·引入命名空间
<%@ Import Namespace="System.IO" %>
这是很白痴的问题,找了好半天,后来把以前的关于1.x的书翻出来,才弄明白。
·类定义
在代码分离模式下,需要"public class XXX",现在代码不分离,就不知道怎么用了。后来经过测试发现,在没有采用代码分离的情况下,不能在.aspx文件里使用class定义类,应该是省略类定义和相应的花括号,直接写上代码。

三、最初的版本
这里面的功能,主要就是模仿asp时代Fso的代码。

<table width="500" border="1" style="border-collapse:collapse;" bordercolor="#cccccc">
            <tr>
                <td style="width: 102px">文件名
                </td>
                <td style="width: 100px">文件大小(字节)
                </td>
                <td>上传时间
                </td>
            </tr>
           
  <%
      string dirString = @"~/Upload/";
      string dirPath = Server.MapPath(@"~/Upload/");
      System.IO.DirectoryInfo Dir = new System.IO.DirectoryInfo(dirPath);//根据路径获取文件夹
      System.IO.FileInfo[] FileArr = Dir.GetFiles();//所有文件,数组
     
 
      //for (int i = 0; i < 5; i++)
      foreach (System.IO.FileInfo f in FileArr) 
      {
       %>  
            <tr>
                <td style="width: 102px; height: 19px;"><a href="<%= "upload/" + f.Name.ToString() %>" target="_blank"><%= f.Name.ToString() %></a>
                </td>
                <td style="width: 100px; height: 19px;"><%= f.Length %>
                </td>
                <td style="height: 19px"><%= f.LastWriteTime %>
                </td>
            </tr>
<%

      }
 %>
 <tr><td colspan="3" rowspan="3"><%= "一共有文件:" + FileArr.Length %></td>
 
 </tr>
        </table>

四、属性/传递参数
好像有句话说,需求推动事物的发展。是的,仅仅如此,似乎也是没有必要做成控件。
以后在实际开发过程中,上传的文件,其实可能会存在于多个目录,所以这个控件得有更好的封装性。其中一条就是,可以不能只限制为一个目录。
那么首先我想到的就是在调用控件的webform上传递参数。请看下面两段代码:
        <uc2:ListUpload_v1 id="ListUpload_v1_1" runat="server"></uc2:ListUpload_v1>
        <uc2:ListUpload_v1 id="ListUpload_v1_2" runat="server" strDir="section"></uc2:ListUpload_v1>
下面这行,比上面就多出来一个属性“strDir”。就是通过个属性把控件指向了另外一个目录:"section"。这就是传递参数的解决办法。
实际上,在asp.net 2.0里,用户组件就是一个类,在调用的页面上,把这个类实例化了。上面的strDir,既然<uc1>这个标签的属性,也是用户组件类的属性。
所以经过修改,V1的代码现在是这样:

<%@ Control Language="C#" ClassName="ListUpload_v1" %>

<script runat="server">
    /****************************************************************
     **上传文件管理控件
     **文件名:ListUpload2.ascx 
     **Copyrigth(c) 2008-2010   *************** 柳城别日 xpnew.cnblogs.com
     **版本号:v1.2 
     **文件编号:
     **创建人:柳城别日
     **日期:2008年5月22日
     **修改人:柳城别日
     **日期:2008年5月22日
     * 描述:用来管理上传文件,支持列表、删除
     **/
      
   
    private string _strDir = @"~/Upload/";//定义默认值


    public string strDir
    {
        get
        {
            return _strDir;
        }
        set
        {
            _strDir = value;
        }

    }

     
</script>

<table border="1" bordercolor="#cccccc" style="border-collapse: collapse" width="500">
    <tr>
        <td style="width: 20px">
            选</td>
        <td style="width: 102px">
            文件名
        </td>
        <td style="width: 100px">
            文件大小(字节)
        </td>
        <td>
            上传时间
        </td>
    </tr>
    <%
        string dirString = strDir;//我比较喜欢用public属性
        string dirPath = Server.MapPath(dirString);
        System.IO.DirectoryInfo Dir = new System.IO.DirectoryInfo(dirPath);
        System.IO.FileInfo[] FileArr = Dir.GetFiles();      
      foreach (System.IO.FileInfo f in FileArr) 
      {
    %>
    <tr>
        <td style="width: 20px; height: 49px">
            <asp:CheckBox ID="FileName" runat="server" /></td>
        <td style="width: 102px; height: 49px">
            <a href='<%= "upload/" + f.Name.ToString() %>' target="_blank">
                <%= f.Name.ToString() %>
            </a>
        </td>
        <td style="width: 100px; height: 49px">
            <%= f.Length %>
        </td>
        <td style="height: 49px">
            <%= f.LastWriteTime %>
            <asp:HiddenField ID="HiddenField1" runat="server" />
        </td>
    </tr>
    <%

      }
    %>
    <tr>
        <td colspan="4" rowspan="1" style="height: 19px">
            <asp:Button ID="DELFILE" runat="server" Text="删除" />
            <asp:Label ID="Msg" runat="server"></asp:Label></td>
    </tr>
    <tr>
        <td colspan="4" rowspan="1" style="height: 19px">
            <%= "一共有文件:" + FileArr.Length %>
        </td>
    </tr>
</table>

五、困惑
到这里,控件的一部分功能,已经完成了。但是接下来的情况就让我困惑了:我得怎么删除文件呢?
翻书、上网搜索,一直没有头绪,Button控件的onClick事件,我不知道怎么才能取得checkbox的值!
最开始的时候,曾经尝试“checkbox.Value”,这样的东西,编译的时候,vs提示错误:“System.Web.UI.WebControls.CheckBox”并不包含“Value”的定义”。
确实,checkbox没有这个属性,网上也有人建议用checkbox.Text(这个办法在这里不可行,最终在浏览器显示的不好看。)
在万般无奈、胡思乱想当中,想到了把数据绑定到checkbox上,于是在网上找到了把数组绑定到DataGrid的资料,研究下去,渐渐地就跑题了。而结果这个控件出了四个版本!(当然收获还是蛮巨大的)
再后来,又想要开发自定义组件,组件开发出来了,可是组件开发出来之后却发现,不能动态为组件设置属性。绕了好长一大圈之后,又回到了原点。
六、再次回归asp
接下来,我只好尝试回归到asp的办法,使用html控件,提交表单了。研究发现,在用户控件里面,不能包含<form>标签。好像在asp.net里面一个webform实际上就是一个form,至少在我看到的所有的代码里面就是这样。而在webform里面,同一个用户控件就有可能出现多个实例,那么在用户控件里使用的html控件,就会出现一种特殊的重复。在用户控件内部,html控件很可能是重复的,在用户控件外部,也有可能会和另外一个控件中的html控件重复,还有可能会在同一控件的不同实例之间重复。就拿后一种情况来说,测试的时候,我在测试用的webform里放置了两个上传文件管理控件,一个是针对upload目录,另一个是针对section目录。如果我在用户控件里面放置的Input(checkbox)控件name属性如果设置为"Filename",那么提交表单的时候,引用的Request.Form["Filename"],就有可能同时包含了upload目录和section目录的文件。在有些场合下,这么做可能会很有用,但是我担心会出现一些不可预料的问题,所以我决定,还是得要避免“html控件在用户控件外部出现重复”这种情况。
在我之前的研究中发现,用户控件在webform显示给客户端浏览器的时候,都会加上用户控件实例的名称。比如说,我前没尝试使用checkbox控件的时候,在.ascx文件里指定ID为"file_name",那么在浏览器里就可能有变成id="ListUpload_v1_1$file_name"。为了避免前述的控件外部重复,我就打起了实列名称的主意。
面向对象太深的原理,我不懂,但是我相信一个类、一个控件在实例化之后都有一个名称、编号(会不会有指针?)。那么在编程的时候怎么样才能获得这个类被实例化以后,它的名称和编号?这个问题,在我半年前开发OO的JavaScript时,困扰了很久却仍未有结果,不过今天不一样了,今天用的是C#,万岁C#!没怎么费力,我试着输入this.i之后,vs2005打开了代码提示,并替我指向其中的ID,万岁vs2005!
于是这个html控件就做好了:
<input type="checkbox" name="<%= ID %>_Filname" value="<%=f.Name %>"/>
然后是获取表单:
        String fullCheckBoxName = ID + "_Filname";
        string[] form = Context.Request.Form[fullCheckBoxName].Split(',');//将string拆分成数组
获取表单的时候,和开发asp是不一样的,详细情况我在代码里面有说明。
再之后,就是删除文件了。

六、后记:
到此为止,我这个“上传文件管理控件v1版”,基本功能就开发完毕了。实际项目中,这种asp模式基本上是没有什么用的,但是通过这个东西却让我学习了不少知识,开辟了通向v2 v3 v4的道路。
另外注意,这里也并不是最完整的功能,离实际应用还差一些。从方便性来讲,还应该加上“全选”这样的功能。从安全性来讲,这个控件可以删除服务器上任何目录里的文件(只要asp.net进程和IIS有权限),它也是一个安全隐患。所以最终的版本里,我会限制一下,strDir属性只能指向有限的几个目录。
附:源代码下载https://files.cnblogs.com/xpnew/ListUpload_v1.rar
我的博客:柳城别日http://xpnew.cnblogs.com 我的邮箱:xpnew@126.com
[注:2008年5月28日修改几个错别字,有一些新的情况出现,但是我得把一系列文章都写完再修改内容方面的问题。]

posted @ 2008-05-24 05:38  柳城之城  阅读(1920)  评论(2编辑  收藏  举报