ASP.NET初学笔记:FileUpload控件

  • FileUpLoad控件用于用户向Web应用程序上传文件。文件上传后,可以把文件保存在任意地方,通常把文件保存在文件系统或数据库。向页面添加FileUpLoad控件会自动地为服务器的<form>标签添加enctype="multipart/form-data"属性。

1.  把文件保存到文件系统

    • 以下代码页面展示了如何使用FileUpLoad控件把图片上传到应用程序。
      [c-sharp] view plaincopy
      1. <%@ Page Language="C#" %>  
      2. <%@ Import Namespace="System.IO" %>  
      3. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
      4. <script runat="server">  
      5.     protected void btnAdd_Click(object sender, EventArgs e)  
      6.     {  
      7.         if (upImage.HasFile)  
      8.         {  
      9.             if (CheckFileType(upImage.FileName))  
      10.             {  
      11.                 //用FileUpload.FileName属性得到上传文件名,也可以使用HttpPostedFile.FileName得到。  
      12.                 string filePath = "~/UploadImages/" + upImage.FileName;  
      13.                 //MapPath方法,检索虚拟路径(绝对的或相对的)或应用程序相关的路径映射到的物理路径。  
      14.                 //FileUpload.SavaAs()方法用于把上传文件保存到文件系统中,也可以使用HttpPostedFile.SaveAs()方法。  
      15.                 upImage.SaveAs(MapPath(filePath));  
      16.             }  
      17.         }  
      18.     }  
      19.     bool CheckFileType(string fileName)  
      20.     {  
      21.         //GetExtension()方法返回指定的路径字符串的扩展名。  
      22.         //Path类位于System.IO命名空间中,用于对包含文件或目录路径信息的 String 实例执行操作。  
      23.         string ext = Path.GetExtension(fileName);  
      24.         switch (ext.ToLower())  
      25.         {  
      26.             case ".gif"return true;  
      27.             case ".png"return true;  
      28.             case ".jpg"return true;  
      29.             case ".jpeg"return true;  
      30.             defaultreturn false;  
      31.         }  
      32.     }  
      33.     void Page_PreRender()  
      34.     {  
      35.         string upFolder = MapPath("~/UploadImages/");  
      36.         //在指定的路径中初始化DirectoryInfo类的新实例,DirectoryInfo类公开用于创建、移动和枚举目录和子目录的实例方法。  
      37.         DirectoryInfo dir = new DirectoryInfo(upFolder);  
      38.         //GetFiles,返回当前目录的文件列表。  
      39.         dlstImages.DataSource = dir.GetFiles();  
      40.         dlstImages.DataBind();  
      41.     }  
      42. </script>  
      43.   
      44. <html xmlns="http://www.w3.org/1999/xhtml">  
      45. <head runat="server">  
      46.     <title>无标题页</title>  
      47. </head>  
      48. <body>  
      49.     <form id="form1" runat="server">  
      50.     <div>  
      51.         <asp:Label ID="lblImageFile" runat="server" Text="Image File" AssociatedControlID="upImage"></asp:Label>  
      52.         <asp:FileUpload ID="upImage" runat="server" /><br />  
      53.         <asp:Button ID="btnAdd" runat="server" Text="Add Image" OnClick="btnAdd_Click"/><hr />  
      54.         <asp:DataList ID="dlstImages" RepeatColumns="3" runat="server">  
      55.             <ItemTemplate>  
      56.                 <asp:Image ID="Image1" runat="server" style="width:200px"  
      57.                     ImageUrl='<%# Eval("Name","~/UploadImages/{0}") %>' />  
      58.                  <br />  
      59.                  <%# Eval("Name") %>  
      60.                  <!--Name,对于文件,获取该文件的名称。对于目录,如果存在层次结构,则获取层次结构中最后一个目录的名称。否则,Name 属性获取该目录的名称。 -->  
      61.             </ItemTemplate>  
      62.         </asp:DataList>  
      63.     </div>  
      64.     </form>  
      65. </body>  
      66. </html>  

      效果:

      • 为在系统中保存文件,asp.net页面关联的Windows帐户必须有足够的权限来保存文件。

      2.  把文件保存到数据库

      • 也可以用FileUpload控件把文件保存到数据库表。在数据库中保存和检索文件会给服务器增加更多压力。但是它也有些优点,首先,可以避免系统权限问题,其次,能更方便地备份信息。下面演示的是如何把Word文档保存到数据库表中。
        [c-sharp] view plaincopy
        1. <%@ Page Language="C#" %>  
        2. <%@ Import Namespace="System.IO" %>  
        3. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
        4.   
        5. <script runat="server">  
        6.     protected void btnAdd_Click(object sender, EventArgs e)  
        7.     {  
        8.         if (upFile.HasFile)  
        9.         {  
        10.             //调用SqlDataSource控件的Insert()方法把FileUpLoad控件的FileName和FileBytes属性的值插入本地SQL Express数据库表。  
        11.             //Insert()方法使用InsertCommand SQL字符串和InsertParameters集合中的所有参数执行插入操作。  
        12.             if (CheckFileType(upFile.FileName))  
        13.                 srcFiles.Insert();  
        14.         }  
        15.     }  
        16.     bool CheckFileType(string fileName)  
        17.     {  
        18.         return Path.GetExtension(fileName).ToLower() == ".doc";  
        19.     }  
        20. </script>  
        21.   
        22. <html xmlns="http://www.w3.org/1999/xhtml">  
        23. <head runat="server">  
        24.     <title>无标题页</title>  
        25. </head>  
        26. <body>  
        27.     <form id="form1" runat="server">  
        28.     <div>  
        29.         <asp:Label ID="lblFile" runat="server" AssociatedControlID="upFile" Text="Word Document"></asp:Label>  
        30.         <asp:FileUpload ID="upFile" runat="server" />  
        31.         <asp:Button ID="btnAdd" runat="server" Text="Add Document" OnClick="btnAdd_Click" /><hr />  
        32.           
        33.         <asp:Repeater ID="rptFiles" DataSourceID="srcFiles" runat="server">  
        34.         <ItemTemplate><li>  
        35.         <asp:HyperLink ID="lnkFile" Text='<%Eval("FileName")%>' NavigateUrl='<%Eval("Id","~/FileHandler.ashx?id={0}") %>' runat="server" /></li>  
        36.         </ItemTemplate>  
        37.         </asp:Repeater>  
        38.           
        39.         <asp:SqlDataSource ID="srcFiles" runat="server"  
        40.             ConnectionString="Server=./SQLExpress;Integrated Security=True;  
        41.             AttachDbFilename=|DataDirectory|FilesDB.mdf;User Instance=True"  
        42.             SelectCommand="select Id,FileName from Files"  
        43.             InsertCommand="insert Files(FileName,FileBytes) values (@FileName,@FileBytes)" >  
        44.             <InsertParameters>  
        45.                <asp:ControlParameter Name="FileName" ControlID="upFile" PropertyName="FileName" />  
        46.                <asp:ControlParameter Name="FileBytes" ControlID="upFile" PropertyName="FileBytes" />  
        47.             </InsertParameters>  
        48.         </asp:SqlDataSource>  
        49.         <!--InsertParameters从与SqlDataSource控件相关联的对象获取包含InsertCommand属性所使用的参数的参数集合。-->  
        50.         <!--ControlParameter对象用于将参数设置为ASP.NET网页中的控件的属性值。  
        51.             使用ControlID属性指定控件。使用PropertyName属性指定提供参数值的属性的名称。-->  
        52.         <!--FileBytes以字节数组形式获取上传文件内容。-->  
        53.     </div>  
        54.     </form>  
        55. </body>  
        56. </html>  

        效果:

        页面显示了当前在数据库中Word文档的列表。可以点击其中的文件查看文件内容。点击文档名会链接到FileHandler.ashx页面,这个文件是一个普通的HTTP处理程序文件。当有人用特定的路径请求文件时,HTTP处理程序用于执行代码。下面是FileHandler.ashx文件的代码。
        [c-sharp] view plaincopy
        1. <%@ WebHandler Language="C#" Class="FileHandler" %>  
        2. using System;  
        3. using System.Web;  
        4. using System.Data;  
        5. using System.Data.SqlClient;  
        6. public class FileHandler : IHttpHandler {  
        7.     const string conString = @"Server=./SQLExpress;Integrated Security=True;  
        8.         AttachDbFileName=|DataDirectory|FilesDB.mdf;User Instance=True";   
        9.     public void ProcessRequest (HttpContext context) {  
        10.         context.Response.ContentType = "application/msword";        
        11.         SqlConnection con = new SqlConnection(conString);  
        12.         SqlCommand cmd = new SqlCommand("SELECT FileBytes FROM Files WHERE Id=@Id", con);  
        13.         cmd.Parameters.AddWithValue("@Id", context.Request["Id"]);  
        14.         using (con)  
        15.         {  
        16.             con.Open();  
        17.             byte[] file = (byte[])cmd.ExecuteScalar();  
        18.             context.Response.BinaryWrite(file);  
        19.         }  
        20.     }  
        21.     public bool IsReusable {  
        22.         get {  
        23.             return false;  
        24.         }  
        25.     }  
        26.   
        27. }  
        当请求FileHandler.ashx页面时,执行了ProcessRequest()方法。该方法取得名为Id的查询字符串项,并从数据库表Files中获取匹配的记录。数据库记录包含Word文档内容字节数组。再用Response.BinaryWrite()方法把字节数组发送到浏览器。
        关于HTTP处理程序,现在还没学,上面的FileHandler.ashx页面的代码大部分都不太理解,等以后学到之方面的内容了,再回来解析一下吧。

      3.  上传大文件

      • 上传大文件时,需要做一些额外工作。你可能不希望把服务器端的所有内存都消耗在容纳整个文件上。处理大文件时,需要使用多个可托管(manageable)内存块来处理文件。
        首先,为了处理大文件需要配置应用程序。有两个配置项影响着向服务器提交大文件:httpRuntime maxRequestLength和httpRuntime requestLengthDiskThreshold。maxRequestLength指定提交的表单能被服务器端接收的最大值。默认不能提交大于4MB的表单,否则会得到一个异常。如果要上传超过4MB的文件就需要更改该配置。requestLengthDiskThreshold决定如何把上表单缓存在文件系统。asp.net framework可以把大的文件缓存在文件系统中,当文件大小超过requestLengthDiskThreshold设置后,文件的余下部分被缓存在文件系统(asp.net临时文件夹中)。默认情况下,把超过80KB的提交数据缓存到文件缓存器中,可以修改requestLengthDiskThreshold来设置新的阈值(requestLengthDiskThreshold设置的值必须小于maxRequestLength设置值)。下面代码的Web配置文件设置为可以上传不超过10MB的文件,并把缓存阈值改为100kB。
        <configuration>
          <system.web>
            <httpRuntime maxRequestLength="10240" requestLengthDiskThreshold="100"/>
          </system.web>
        </configuration>
        下面代码演示如何高效地把一个大文件存储到数据库表中。
        [c-sharp] view plaincopy
        1. <%@ Page Language="C#" %>  
        2. <%@ Import Namespace="System.IO" %>  
        3. <%@ Import Namespace="System.Data" %>  
        4. <%@ Import Namespace="System.Data.SqlClient" %>  
        5. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
        6. <script runat="server">  
        7.     const string conString = @"Server=./SQLExpress;Integrated Security=True;AttachDbFilename=|DataDirectory|FilesDB.mdf;User Instance=True";  
        8.     //@在c#中表示后面的字符串强制不转义,在里面的转义字符无效  
        9.     void btnAdd_Click(object sender, EventArgs e)  
        10.     {  
        11.         if (upFile.HasFile)  
        12.         {  
        13.             if (CheckFileType(upFile.FileName))  
        14.             {  
        15.                 AddFile(upFile.FileName, upFile.FileContent);  
        16.                 //FileUpload.FileContent以流(stream)形式获取上传文件内容。  
        17.                 rptFiles.DataBind();  
        18.             }  
        19.         }  
        20.     }  
        21.     bool CheckFileType(string fileName)  
        22.     {  
        23.         return Path.GetExtension(fileName).ToLower() == ".doc";  
        24.     }  
        25.     //Stream类是所有流的抽象基类,提供字节序列的一般视图。  
        26.     void AddFile(string fileName, Stream upload)  
        27.     {  
        28.         SqlConnection con = new SqlConnection(conString);  
        29.         SqlCommand cmd = new SqlCommand("INSERT Files (FileName) Values (@FileName);" + "SELECT @Identity=SCOPE_IDENTITY()", con);  
        30.         //SELECT @Identity=SCOPE_IDENTITY()声明一个参数,在后面传入一个值,可以避免SQL注入,比直接拼SQL语句好  
        31.         //获取添加记录的自增ID。  
        32.         cmd.Parameters.AddWithValue("@FileName", fileName);  
        33.         //SqlCommand.Parameters用于获取SqlParameterCollection。  
        34.         //SqlParameterCollection表示与 SqlCommand 相关联的参数的集合以及各个参数到 DataSet 中列的映射。  
        35.         //AddWithValue将一个值添加到 SqlParameterCollection 的末尾。  
        36.         SqlParameter idParm = cmd.Parameters.Add("@Identity", SqlDbType.Int);  
        37.         idParm.Direction = ParameterDirection.Output;  
        38.   
        39.         using (con)  
        40.         {  
        41.             con.Open();  
        42.             cmd.ExecuteNonQuery();  
        43.             int newFileId = (int)idParm.Value;  
        44.             StoreFile(newFileId, upload, con);  
        45.         }  
        46.         //在using作用域之后自动释放对象(调用Dispose方法),相当于con.close()。  
        47.     }  
        48.       
        49.     void StoreFile(int fileId, Stream upload, SqlConnection connection)  
        50.     {  
        51.         //从流br内读取长度为8040的数据  
        52.         int bufferLen = 8040;  
        53.         BinaryReader br = new BinaryReader(upload);  
        54.         byte[] chunk = br.ReadBytes(bufferLen);  
        55.           
        56.         //修改数据表Files内Id为fileId的数据流为chunk  
        57.         SqlCommand cmd = new SqlCommand("UPDATE Files SET FileBytes=@Buffer WHERE Id=@FileId", connection);  
        58.         cmd.Parameters.AddWithValue("@FileId", fileId);  
        59.         cmd.Parameters.Add("@Buffer", SqlDbType.VarBinary, bufferLen).Value = chunk;  
        60.         cmd.ExecuteNonQuery();  
        61.   
        62.         //###AAA###追加数据表Files内Id为fileId的数据流  
        63.         SqlCommand cmdAppend = new SqlCommand("UPDATE Files SET FileBytes .WRITE(@Buffer, NULL, 0) WHERE Id=@FileId", connection);  
        64.         cmdAppend.Parameters.AddWithValue("@FileId", fileId);  
        65.         cmdAppend.Parameters.Add("@Buffer", SqlDbType.VarBinary, bufferLen);  
        66.         chunk = br.ReadBytes(bufferLen);  
        67.   
        68.         //当br流中有可读数据时,追加至###AAA###  
        69.         while (chunk.Length > 0)  
        70.         {  
        71.             cmdAppend.Parameters["@Buffer"].Value = chunk;  
        72.             cmdAppend.ExecuteNonQuery();  
        73.             chunk = br.ReadBytes(bufferLen);  
        74.         }  
        75.         br.Close(); //关闭流  
        76.     }  
        77. </script>  
        78.   
        79. <html xmlns="http://www.w3.org/1999/xhtml">  
        80. <head runat="server">  
        81.     <title>无标题页</title>  
        82. </head>  
        83. <body>  
        84.     <form id="form1" runat="server">  
        85.     <div>  
        86.     <asp:Label ID="lblFile" runat="server" AssociatedControlID="upFile" Text="Word Document"></asp:Label>  
        87.         <asp:FileUpload ID="upFile" runat="server" />  
        88.         <asp:Button ID="btnAdd" runat="server" Text="Add Document" OnClick="btnAdd_Click" /><hr />  
        89.           
        90.         <asp:Repeater ID="rptFiles" DataSourceID="srcFiles" runat="server">  
        91.         <ItemTemplate>  
        92.         <li>  
        93.         <asp:HyperLink ID="lnkFile" Text='<%#Eval("FileName")%>' NavigateUrl='<%#Eval("Id","~/FileHandlerLarge.ashx?id={0}") %>' runat="server" />  
        94.         </li>  
        95.         </ItemTemplate>  
        96.         </asp:Repeater>  
        97.           
        98.         <asp:SqlDataSource ID="srcFiles" runat="server"  
        99.             ConnectionString="Server=./SQLExpress;Integrated Security=True;  
        100.             AttachDbFilename=|DataDirectory|FilesDB.mdf;User Instance=True"  
        101.             SelectCommand="select Id,FileName from Files" >  
        102.         </asp:SqlDataSource>  
        103.     </div>  
        104.     </form>  
        105. </body>  
        106. </html>  
        在上面代码中,首先调用了AddFile()方法,该方法在包含文件名字段的数据库表Files中新增一行。然后调用StoreFile()方法,该方法把上传文件的实际字节添加到数据库中。上传文件被分成8040字节大小的块。注意在更新数据库字段FileBytes时,SQL UPDATE语句包含.WRITE字句。该代码中的页面不会在内存中装整个上传文件,上传文件以8040字节大小的块从文件系统中取出,产一块一块存入SQL Server中。
        点击文件名将执行FileHandle.aspx HTTP处理程序,该项处理程序从数据库中获取先中的文件并把它发送到浏览器。下面代码包含了这个处理程序。
        [c-sharp] view plaincopy
        1. <%@ WebHandler Language="C#" Class="FileHandlerLarge" %>  
        2. using System;  
        3. using System.Web;  
        4. using System.Data;  
        5. using System.Data.SqlClient;  
        6. public class FileHandlerLarge : IHttpHandler {  
        7.     const string conString = @"Server=./SQLExpress;Integrated Security=True;  
        8.         AttachDbFileName=|DataDirectory|FilesDB.mdf;User Instance=True";  
        9.     public void ProcessRequest (HttpContext context) {  
        10.         context.Response.Buffer = false;  
        11.         context.Response.ContentType = "application/msword";  
        12.         SqlConnection con = new SqlConnection(conString);  
        13.         SqlCommand cmd = new SqlCommand("SELECT FileBytes FROM Files WHERE Id=@Id", con);  
        14.         cmd.Parameters.AddWithValue("@Id", context.Request["Id"]);  
        15.         using (con)  
        16.         {  
        17.             con.Open();  
        18.             SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);  
        19.             if (reader.Read())  
        20.             {  
        21.                 int bufferSize = 8040;  
        22.                 byte[] chunk = new byte[bufferSize];  
        23.                 long retCount;  
        24.                 long startIndex = 0;  
        25.   
        26.                 retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);  
        27.   
        28.                   
        29.                 while (retCount == bufferSize)  
        30.                 {  
        31.                     context.Response.BinaryWrite(chunk);  
        32.   
        33.                     startIndex += bufferSize;  
        34.                     retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);  
        35.                 }  
        36.   
        37.                 byte[] actualChunk = new Byte[retCount - 1];  
        38.                 Buffer.BlockCopy(chunk, 0, actualChunk, 0, (int)retCount - 1);  
        39.                 context.Response.BinaryWrite(actualChunk);  
        40.                                  
        41.             }  
        42.         }  
        43.     }  
        44.    
        45.     public bool IsReusable {  
        46.         get {  
        47.             return false;  
        48.         }  
        49.     }  
        50.   
        51. }  
        上面代码中的HTTP处理程序使用SqlDataReader从数据库中获取文件。注意,SqlDataReader使用CommandBehavior.SequentialAccess参数获取数据。这个参数使SqlDataReader以流的方式加载数据。数据库字段的内容以8040字节大小的块取入内存中,这些内容块用Response.BinaryWrite()方法写入浏览器。另外注意这个处理程序禁止用了响应缓存,Response.Buffer属性的值被设成False。由于禁用了缓存,处理程序输出就不会在传输到浏览器前缓在服务器端的内存中。
posted @ 2012-12-03 17:35  DaBan  阅读(2393)  评论(0编辑  收藏  举报