ASP.NET初学笔记:FileUpload控件
- FileUpLoad控件用于用户向Web应用程序上传文件。文件上传后,可以把文件保存在任意地方,通常把文件保存在文件系统或数据库。向页面添加FileUpLoad控件会自动地为服务器的<form>标签添加enctype="multipart/form-data"属性。
1. 把文件保存到文件系统
- 以下代码页面展示了如何使用FileUpLoad控件把图片上传到应用程序。
- <%@ Page Language="C#" %>
- <%@ Import Namespace="System.IO" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <script runat="server">
- protected void btnAdd_Click(object sender, EventArgs e)
- {
- if (upImage.HasFile)
- {
- if (CheckFileType(upImage.FileName))
- {
- //用FileUpload.FileName属性得到上传文件名,也可以使用HttpPostedFile.FileName得到。
- string filePath = "~/UploadImages/" + upImage.FileName;
- //MapPath方法,检索虚拟路径(绝对的或相对的)或应用程序相关的路径映射到的物理路径。
- //FileUpload.SavaAs()方法用于把上传文件保存到文件系统中,也可以使用HttpPostedFile.SaveAs()方法。
- upImage.SaveAs(MapPath(filePath));
- }
- }
- }
- bool CheckFileType(string fileName)
- {
- //GetExtension()方法返回指定的路径字符串的扩展名。
- //Path类位于System.IO命名空间中,用于对包含文件或目录路径信息的 String 实例执行操作。
- string ext = Path.GetExtension(fileName);
- switch (ext.ToLower())
- {
- case ".gif": return true;
- case ".png": return true;
- case ".jpg": return true;
- case ".jpeg": return true;
- default: return false;
- }
- }
- void Page_PreRender()
- {
- string upFolder = MapPath("~/UploadImages/");
- //在指定的路径中初始化DirectoryInfo类的新实例,DirectoryInfo类公开用于创建、移动和枚举目录和子目录的实例方法。
- DirectoryInfo dir = new DirectoryInfo(upFolder);
- //GetFiles,返回当前目录的文件列表。
- dlstImages.DataSource = dir.GetFiles();
- dlstImages.DataBind();
- }
- </script>
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head runat="server">
- <title>无标题页</title>
- </head>
- <body>
- <form id="form1" runat="server">
- <div>
- <asp:Label ID="lblImageFile" runat="server" Text="Image File" AssociatedControlID="upImage"></asp:Label>
- <asp:FileUpload ID="upImage" runat="server" /><br />
- <asp:Button ID="btnAdd" runat="server" Text="Add Image" OnClick="btnAdd_Click"/><hr />
- <asp:DataList ID="dlstImages" RepeatColumns="3" runat="server">
- <ItemTemplate>
- <asp:Image ID="Image1" runat="server" style="width:200px"
- ImageUrl='<%# Eval("Name","~/UploadImages/{0}") %>' />
- <br />
- <%# Eval("Name") %>
- <!--Name,对于文件,获取该文件的名称。对于目录,如果存在层次结构,则获取层次结构中最后一个目录的名称。否则,Name 属性获取该目录的名称。 -->
- </ItemTemplate>
- </asp:DataList>
- </div>
- </form>
- </body>
- </html>
效果:
-
- 为在系统中保存文件,asp.net页面关联的Windows帐户必须有足够的权限来保存文件。
2. 把文件保存到数据库
- 也可以用FileUpload控件把文件保存到数据库表。在数据库中保存和检索文件会给服务器增加更多压力。但是它也有些优点,首先,可以避免系统权限问题,其次,能更方便地备份信息。下面演示的是如何把Word文档保存到数据库表中。
- <%@ Page Language="C#" %>
- <%@ Import Namespace="System.IO" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <script runat="server">
- protected void btnAdd_Click(object sender, EventArgs e)
- {
- if (upFile.HasFile)
- {
- //调用SqlDataSource控件的Insert()方法把FileUpLoad控件的FileName和FileBytes属性的值插入本地SQL Express数据库表。
- //Insert()方法使用InsertCommand SQL字符串和InsertParameters集合中的所有参数执行插入操作。
- if (CheckFileType(upFile.FileName))
- srcFiles.Insert();
- }
- }
- bool CheckFileType(string fileName)
- {
- return Path.GetExtension(fileName).ToLower() == ".doc";
- }
- </script>
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head runat="server">
- <title>无标题页</title>
- </head>
- <body>
- <form id="form1" runat="server">
- <div>
- <asp:Label ID="lblFile" runat="server" AssociatedControlID="upFile" Text="Word Document"></asp:Label>
- <asp:FileUpload ID="upFile" runat="server" />
- <asp:Button ID="btnAdd" runat="server" Text="Add Document" OnClick="btnAdd_Click" /><hr />
- <asp:Repeater ID="rptFiles" DataSourceID="srcFiles" runat="server">
- <ItemTemplate><li>
- <asp:HyperLink ID="lnkFile" Text='<%Eval("FileName")%>' NavigateUrl='<%Eval("Id","~/FileHandler.ashx?id={0}") %>' runat="server" /></li>
- </ItemTemplate>
- </asp:Repeater>
- <asp:SqlDataSource ID="srcFiles" runat="server"
- ConnectionString="Server=./SQLExpress;Integrated Security=True;
- AttachDbFilename=|DataDirectory|FilesDB.mdf;User Instance=True"
- SelectCommand="select Id,FileName from Files"
- InsertCommand="insert Files(FileName,FileBytes) values (@FileName,@FileBytes)" >
- <InsertParameters>
- <asp:ControlParameter Name="FileName" ControlID="upFile" PropertyName="FileName" />
- <asp:ControlParameter Name="FileBytes" ControlID="upFile" PropertyName="FileBytes" />
- </InsertParameters>
- </asp:SqlDataSource>
- <!--InsertParameters从与SqlDataSource控件相关联的对象获取包含InsertCommand属性所使用的参数的参数集合。-->
- <!--ControlParameter对象用于将参数设置为ASP.NET网页中的控件的属性值。
- 使用ControlID属性指定控件。使用PropertyName属性指定提供参数值的属性的名称。-->
- <!--FileBytes以字节数组形式获取上传文件内容。-->
- </div>
- </form>
- </body>
- </html>
效果:
页面显示了当前在数据库中Word文档的列表。可以点击其中的文件查看文件内容。点击文档名会链接到FileHandler.ashx页面,这个文件是一个普通的HTTP处理程序文件。当有人用特定的路径请求文件时,HTTP处理程序用于执行代码。下面是FileHandler.ashx文件的代码。- <%@ WebHandler Language="C#" Class="FileHandler" %>
- using System;
- using System.Web;
- using System.Data;
- using System.Data.SqlClient;
- public class FileHandler : IHttpHandler {
- const string conString = @"Server=./SQLExpress;Integrated Security=True;
- AttachDbFileName=|DataDirectory|FilesDB.mdf;User Instance=True";
- public void ProcessRequest (HttpContext context) {
- context.Response.ContentType = "application/msword";
- SqlConnection con = new SqlConnection(conString);
- SqlCommand cmd = new SqlCommand("SELECT FileBytes FROM Files WHERE Id=@Id", con);
- cmd.Parameters.AddWithValue("@Id", context.Request["Id"]);
- using (con)
- {
- con.Open();
- byte[] file = (byte[])cmd.ExecuteScalar();
- context.Response.BinaryWrite(file);
- }
- }
- public bool IsReusable {
- get {
- return false;
- }
- }
- }
关于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>
下面代码演示如何高效地把一个大文件存储到数据库表中。- <%@ Page Language="C#" %>
- <%@ Import Namespace="System.IO" %>
- <%@ Import Namespace="System.Data" %>
- <%@ Import Namespace="System.Data.SqlClient" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <script runat="server">
- const string conString = @"Server=./SQLExpress;Integrated Security=True;AttachDbFilename=|DataDirectory|FilesDB.mdf;User Instance=True";
- //@在c#中表示后面的字符串强制不转义,在里面的转义字符无效
- void btnAdd_Click(object sender, EventArgs e)
- {
- if (upFile.HasFile)
- {
- if (CheckFileType(upFile.FileName))
- {
- AddFile(upFile.FileName, upFile.FileContent);
- //FileUpload.FileContent以流(stream)形式获取上传文件内容。
- rptFiles.DataBind();
- }
- }
- }
- bool CheckFileType(string fileName)
- {
- return Path.GetExtension(fileName).ToLower() == ".doc";
- }
- //Stream类是所有流的抽象基类,提供字节序列的一般视图。
- void AddFile(string fileName, Stream upload)
- {
- SqlConnection con = new SqlConnection(conString);
- SqlCommand cmd = new SqlCommand("INSERT Files (FileName) Values (@FileName);" + "SELECT @Identity=SCOPE_IDENTITY()", con);
- //SELECT @Identity=SCOPE_IDENTITY()声明一个参数,在后面传入一个值,可以避免SQL注入,比直接拼SQL语句好
- //获取添加记录的自增ID。
- cmd.Parameters.AddWithValue("@FileName", fileName);
- //SqlCommand.Parameters用于获取SqlParameterCollection。
- //SqlParameterCollection表示与 SqlCommand 相关联的参数的集合以及各个参数到 DataSet 中列的映射。
- //AddWithValue将一个值添加到 SqlParameterCollection 的末尾。
- SqlParameter idParm = cmd.Parameters.Add("@Identity", SqlDbType.Int);
- idParm.Direction = ParameterDirection.Output;
- using (con)
- {
- con.Open();
- cmd.ExecuteNonQuery();
- int newFileId = (int)idParm.Value;
- StoreFile(newFileId, upload, con);
- }
- //在using作用域之后自动释放对象(调用Dispose方法),相当于con.close()。
- }
- void StoreFile(int fileId, Stream upload, SqlConnection connection)
- {
- //从流br内读取长度为8040的数据
- int bufferLen = 8040;
- BinaryReader br = new BinaryReader(upload);
- byte[] chunk = br.ReadBytes(bufferLen);
- //修改数据表Files内Id为fileId的数据流为chunk
- SqlCommand cmd = new SqlCommand("UPDATE Files SET FileBytes=@Buffer WHERE Id=@FileId", connection);
- cmd.Parameters.AddWithValue("@FileId", fileId);
- cmd.Parameters.Add("@Buffer", SqlDbType.VarBinary, bufferLen).Value = chunk;
- cmd.ExecuteNonQuery();
- //###AAA###追加数据表Files内Id为fileId的数据流
- SqlCommand cmdAppend = new SqlCommand("UPDATE Files SET FileBytes .WRITE(@Buffer, NULL, 0) WHERE Id=@FileId", connection);
- cmdAppend.Parameters.AddWithValue("@FileId", fileId);
- cmdAppend.Parameters.Add("@Buffer", SqlDbType.VarBinary, bufferLen);
- chunk = br.ReadBytes(bufferLen);
- //当br流中有可读数据时,追加至###AAA###
- while (chunk.Length > 0)
- {
- cmdAppend.Parameters["@Buffer"].Value = chunk;
- cmdAppend.ExecuteNonQuery();
- chunk = br.ReadBytes(bufferLen);
- }
- br.Close(); //关闭流
- }
- </script>
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head runat="server">
- <title>无标题页</title>
- </head>
- <body>
- <form id="form1" runat="server">
- <div>
- <asp:Label ID="lblFile" runat="server" AssociatedControlID="upFile" Text="Word Document"></asp:Label>
- <asp:FileUpload ID="upFile" runat="server" />
- <asp:Button ID="btnAdd" runat="server" Text="Add Document" OnClick="btnAdd_Click" /><hr />
- <asp:Repeater ID="rptFiles" DataSourceID="srcFiles" runat="server">
- <ItemTemplate>
- <li>
- <asp:HyperLink ID="lnkFile" Text='<%#Eval("FileName")%>' NavigateUrl='<%#Eval("Id","~/FileHandlerLarge.ashx?id={0}") %>' runat="server" />
- </li>
- </ItemTemplate>
- </asp:Repeater>
- <asp:SqlDataSource ID="srcFiles" runat="server"
- ConnectionString="Server=./SQLExpress;Integrated Security=True;
- AttachDbFilename=|DataDirectory|FilesDB.mdf;User Instance=True"
- SelectCommand="select Id,FileName from Files" >
- </asp:SqlDataSource>
- </div>
- </form>
- </body>
- </html>
点击文件名将执行FileHandle.aspx HTTP处理程序,该项处理程序从数据库中获取先中的文件并把它发送到浏览器。下面代码包含了这个处理程序。- <%@ WebHandler Language="C#" Class="FileHandlerLarge" %>
- using System;
- using System.Web;
- using System.Data;
- using System.Data.SqlClient;
- public class FileHandlerLarge : IHttpHandler {
- const string conString = @"Server=./SQLExpress;Integrated Security=True;
- AttachDbFileName=|DataDirectory|FilesDB.mdf;User Instance=True";
- public void ProcessRequest (HttpContext context) {
- context.Response.Buffer = false;
- context.Response.ContentType = "application/msword";
- SqlConnection con = new SqlConnection(conString);
- SqlCommand cmd = new SqlCommand("SELECT FileBytes FROM Files WHERE Id=@Id", con);
- cmd.Parameters.AddWithValue("@Id", context.Request["Id"]);
- using (con)
- {
- con.Open();
- SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
- if (reader.Read())
- {
- int bufferSize = 8040;
- byte[] chunk = new byte[bufferSize];
- long retCount;
- long startIndex = 0;
- retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);
- while (retCount == bufferSize)
- {
- context.Response.BinaryWrite(chunk);
- startIndex += bufferSize;
- retCount = reader.GetBytes(0, startIndex, chunk, 0, bufferSize);
- }
- byte[] actualChunk = new Byte[retCount - 1];
- Buffer.BlockCopy(chunk, 0, actualChunk, 0, (int)retCount - 1);
- context.Response.BinaryWrite(actualChunk);
- }
- }
- }
- public bool IsReusable {
- get {
- return false;
- }
- }
- }