在 ASP.NET 2.0 中上传文件

 

比尔·埃夫金
Reuters

2005 年 12 月

适用于:

   Microsoft ASP.NET 2.0
   Microsoft Visual Web Developer 2005 Express Edition

总结: 了解如何在 Microsoft ASP.NET 2.0 中使用新的 FileUpload 服务器控件。 ) (18 页打印页

目录

简介
FileUpload 服务器控件的示例
结论

简介

自 Microsoft ASP.NET 版本 1.0 引入以来,一直采用内置方法生成能够将文件上传到托管服务器的 Web 应用程序。 这是通过使用 文件字段 HTML 服务器控件完成的。 我之前撰写了一篇 MSDN 文章,介绍如何在 ASP.NET 应用程序中有效使用此控件。 本文将重新介绍文件上传过程,但我将介绍如何有效使用通过 ASP.NET 2.0 提供的新 FileUpload 服务器控件,而不是使用“文件字段”控件。

请务必注意,虽然本文介绍新的 FileUpload 服务器控件,但目前仍可以在应用程序中使用 “文件字段” 控件。

FileUpload 服务器控件的示例

使用 ASP.NET 1 中的 “文件字段” 控件时。x 你必须采取一些额外的步骤,使所有内容都对齐并正常工作。 例如,需要自行将 enctype=“multipart/form-data” 添加到页面的 <表单> 元素。 ASP.NET 2.0 中提供的新 FileUpload 服务器控件使文件上传到宿主服务器的过程尽可能简单。

最后,尝试允许访问 HTML <输入类型=“file”> 标记。 此标记用于处理 HTML 表单中的文件数据。 过去,使用经典 ASP (ASP 3.0 或更早的) 时,许多程序员使用第三方组件将文件从客户端上传到服务器。 现在,借助 .NET 和此新控件,可以处理上传。 清单 1 展示了如何使用 FileUpload 控件将文件上传到服务器。

注意 示例代码在 Microsoft Visual Basic 和 C# 中提供。

列表 1. 使用 FileUpload 控件将文件上传到服务器

Visual Basic

<%@ Page Language="VB" %>

<script runat="server">
    Protected Sub Button1_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs)
        If FileUpload1.HasFile Then
            Try
                FileUpload1.SaveAs("C:\Uploads\" & _
                   FileUpload1.FileName)
                Label1.Text = "File name: " & _
                   FileUpload1.PostedFile.FileName & "<br>" & _
                   "File Size: " & _
                   FileUpload1.PostedFile.ContentLength & " kb<br>" & _
                   "Content type: " & _
                   FileUpload1.PostedFile.ContentType
            Catch ex As Exception
                Label1.Text = "ERROR: " & ex.Message.ToString()
            End Try
        Else
            Label1.Text = "You have not specified a file."
        End If
    End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Upload Files</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:FileUpload ID="FileUpload1" runat="server" /><br />
        <br />
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" 
         Text="Upload File" />&nbsp;<br />
        <br />
        <asp:Label ID="Label1" runat="server"></asp:Label></div>
    </form>
</body>
</html>

C#

<%@ Page Language="C#" %>

<script runat="server">
    protected void Button1_Click(object sender, EventArgs e)
    {
        if (FileUpload1.HasFile)
            try
            {
                FileUpload1.SaveAs("C:\\Uploads\\" + 
                     FileUpload1.FileName);
                Label1.Text = "File name: " +
                     FileUpload1.PostedFile.FileName + "<br>" +
                     FileUpload1.PostedFile.ContentLength + " kb<br>" +
                     "Content type: " +
                     FileUpload1.PostedFile.ContentType;
            }
            catch (Exception ex)
            {
                Label1.Text = "ERROR: " + ex.Message.ToString();
            }
        else
        {
            Label1.Text = "You have not specified a file.";
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Upload Files</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:FileUpload ID="FileUpload1" runat="server" /><br />
        <br />
        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" 
         Text="Upload File" />&nbsp;<br />
        <br />
        <asp:Label ID="Label1" runat="server"></asp:Label></div>
    </form>
</body>
</html>

如果查看为页面生成的源代码,则运行此页面时会注意到一些事项。 清单 2 中提供了此源代码。

清单 2. 从 FileUpload 控件生成的源代码

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
   Upload Files
</title></head>
<body>
    <form name="form1" method="post" action="MyFileUpload.aspx" 
     id="form1" enctype="multipart/form-data">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNDcxNTg5NDg3D2QWAgIEDxYCHgdlbmN0eXBlBRNtdWx0aXBhcnQvZm9yb
       S1kYXRhZGQUQEUFMY1+/fp1mnrkbqmVNQIzFA==" />
</div>

    <div>
        <input type="file" name="FileUpload1" id="FileUpload1" /><br />
        <br />
        <input type="submit" name="Button1" value="Upload File" 
         id="Button1" />&nbsp;<br />
        <br />
        <span id="Label1"></span>
    </div>
    
<div>

   <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" 
    value="/wEWAgLB+7jIAwKM54rGBv2Iz6LxVY7jWec0gZMxnuaK2ufq" />
</div></form>
</body>
</html>

首先需要注意的是,由于 FileUpload 控件位于页面上,ASP.NET 2.0 通过添加相应的 enctype 属性代表你修改了页面的 <form> 元素。 你还会注意到 FileUpload 控件已转换为 HTML <输入类型=“file”> 元素。

当清单 1 中的页面运行时,你可以选择一个文件,并通过单击页面上的“上传文件”按钮将其上传到服务器。 对于此示例,我们应介绍一些重要项目,以便你了解使此工作所需的所有部分。 要使清单 1 中的示例正常工作,必须使服务器上的目标文件夹可供 ASP.NET 使用的帐户写入,以便可以将文件保存到指定的文件夹中。

如果你认为 ASP.NET 帐户未启用写入所需文件夹,只需打开 Microsoft Windows 资源管理器并导航到要向其添加此权限的文件夹。 在本例中,右键单击文件夹 (“上传”文件夹) ,然后选择“ 属性”。 在“ 属性 ”对话框中,单击“ 安全 ”选项卡,确保 ASP.NET 计算机帐户包含在列表中,并且具有写入磁盘的适当权限 (请参阅图 1) 。

Aa479405.uploadasp201 (en-us,MSDN.10) .gif

图 1. 查看“上传”文件夹的“安全性”选项卡

如果在“安全性”选项卡下看不到 ASP.NET 计算机帐户,可以通过单击“ 添加 ”按钮并输入 ASPNET (而不在文本区域中) 句点来添加该帐户,如图 2 所示。

Aa479405.uploadasp202 (en-us,MSDN.10) .gif

图 2. 将 ASP.NET 计算机帐户添加到文件夹安全定义

单击“ 确定 ”,将 ASP.NET 计算机帐户添加到列表中。 在此处,请确保为此帐户授予适当的权限,然后单击“ 确定”即可开始操作。

页面上的“提交”按钮会导致 发生Button1_Click 事件。 此事件上传文件,然后显示一条消息,通过发布有关上传的文件的信息来告知上传是否成功。 如果失败,页面将显示一条错误消息,说明上传失败的原因。

通过使用将自身<转换为 input type=“file”>标记的 FileUpload 控件,浏览器会自动在 ASP.NET 页上的文本字段旁边放置一个“浏览”按钮。 无需对任何其他内容进行编程即可发生这种情况。 当最终用户单击“浏览”按钮时,他可以在本地文件系统中导航以查找要上传到服务器的文件。 如图 3 所示。 单击“打开”会将该文件名和文件的路径置于文本字段中。

Aa479405.uploadasp203 (en-us,MSDN.10) .gif

图 3. 选择文件

处理文件大小限制

你可能没有意识到这一点,但可以使用此方法上传的文件的大小有限制。 默认情况下,使用 FileUpload 控件上传到服务器的文件的最大大小约为 4MB。 不能上传大于此限制的任何内容。

但是,.NET 的一个伟大之处是,它通常提供一种绕过限制的方法。 通常可以更改已就位的默认设置。 若要更改此大小限制,请在 C:\WINDOWS\ (Microsoft.NET\Framework\v2.0.50727\CONFIG) ASP.NET 2.0 配置文件夹中找到的 web.config.comments 或应用程序的 web.config 文件中进行一些更改。

在 web.config.comments 文件中,找到名为 <httpRuntime> 的节点,如下所示:

<httpRuntime 
 executionTimeout="110" 
 maxRequestLength="4096" 
 requestLengthDiskThreshold="80" 
 useFullyQualifiedRedirectUrl="false" 
 minFreeThreads="8" 
 minLocalRequestFreeThreads="4" 
 appRequestQueueLimit="5000" 
 enableKernelOutputCache="true" 
 enableVersionHeader="true" 
 requireRootedSaveAsPath="true" 
 enable="true" 
 shutdownTimeout="90" 
 delayNotificationTimeout="5" 
 waitChangeNotification="0" 
 maxWaitChangeNotification="0" 
 enableHeaderChecking="true" 
 sendCacheControlHeader="true" 
 apartmentThreading="false" />

此单个节点中正在进行大量操作,但负责要上传的文件大小的设置是 maxRequestLength 属性。 默认情况下, (KB) 设置为 4096 KB。 只需更改此值即可增加可以上传到服务器的文件的大小。 如果要允许将 10 MB (MB) 文件上传到服务器,请将 maxRequestLength 值设置为 11264,这意味着应用程序允许将最大 11000 KB 的文件上传到服务器。

在 web.config.comments 文件中进行此更改会将此设置应用于服务器上的所有应用程序。 如果只想将此应用于正在使用的应用程序,请将此节点应用于应用程序的 web.config 文件,并覆盖 web.config.comments 文件中的任何设置。 确保此节点驻留在配置文件中的 system.web> 节点之间<

要上传的文件的大小限制涉及的另一个设置是,为 httpRuntime> 节点中的<executionTimeout 属性提供的值。

给定 executionTimeout 属性的值是 ASP.NET 关闭之前允许上传的秒数。 如果要允许将大型文件上传到服务器,则还需要将此值与 maxRequestLength 值一起增加。

增加可上传的文件大小的一个缺点是,有黑客通过向服务器抛出大量请求来攻击服务器。 为了防止这种情况,实际上可以减小允许上传的文件的大小;否则,你可能会发现数百甚至数千个 10 MB 请求命中服务器。

Client-Side上传所允许的文件类型的验证

可以使用多种方法来控制上传到服务器的文件类型。 遗憾的是,没有防弹方法可以保护你免受有人上传被视为恶意的文件的影响。 但是,可以采取一些步骤,使允许最终用户上传文件的过程更易于管理。

可以使用的一种不错方法是使用 ASP.NET 免费提供的 ASP.NET 验证控件。 这些控件使你能够对要上载的文件执行正则表达式检查,以查看文件的扩展名是否是允许上传的文件。

这非常适合允许客户端使用验证控件的浏览器,因为它强制在客户端上完成检查;如果签名不是你允许的签名,则不会将文件上传到服务器。 清单 3 显示了使用验证控件完成此任务的示例。

注意 此处未介绍验证控件的使用。 查看 验证 ASP.NET 服务器控件 ,详细了解验证控件以及如何在 ASP.NET 页中使用它们。

列表 3. 使用验证控件限制上传到服务器的文件类型

<asp:FileUpload ID="FileUpload1" runat="server" /><br />
<br />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" 
 Text="Upload File" />&nbsp;<br />
<br />
<asp:Label ID="Label1" runat="server"></asp:Label>
<asp:RegularExpressionValidator 
 id="RegularExpressionValidator1" runat="server" 
 ErrorMessage="Only mp3, m3u or mpeg files are allowed!" 
 ValidationExpression="^(([a-zA-Z]:)|(\\{2}\w+)\$?)(\\(\w[\w].*))
    +(.mp3|.MP3|.mpeg|.MPEG|.m3u|.M3U)$" 
 ControlToValidate="FileUpload1"></asp:RegularExpressionValidator>
<br />
<asp:RequiredFieldValidator 
 id="RequiredFieldValidator1" runat="server" 
 ErrorMessage="This is a required field!" 
 ControlToValidate="FileUpload1"></asp:RequiredFieldValidator>

此简单 ASP.NET 页使用验证控件,以便最终用户只能将.mp3、.mpeg或 .m3u 文件上传到服务器。 如果文件类型不是这三个选项之一, 则验证 控件会在屏幕上引发异常。 如图 4 所示。

Aa479405.uploadasp204 (en-us,MSDN.10) .gif

图 4。 使用验证控件验证文件类型

使用 验证 控件并不是控制上传到服务器的文件的万无一失的方式。 对于某人来说,更改文件的文件扩展名并不难,这样就可以接受该文件并将其上传到服务器,从而绕过这个简单的安全模型。

添加Server-Side文件类型验证

你刚刚看到了一种简单的方法,可以将一些 ASP.NET 验证服务器控件添加到 ASP.NET 页,以便仅以文本方式) 对文件扩展名 (执行客户端验证。 现在,让我们看看如何在服务器端执行类似的操作。 清单 4 中介绍了这一点。

清单 4. 检查服务器上的文件类型

Visual Basic

    Protected Sub Button1_Click(ByVal sender As Object, _
      ByVal e As System.EventArgs)
        If FileUpload1.HasFile Then
            Dim fileExt As String
            fileExt = System.IO.Path.GetExtension(FileUpload1.FileName)
            
            If (fileExt = ".mp3") Then
                Try
                    FileUpload1.SaveAs("C:\Uploads\" & _
                       FileUpload1.FileName)
                    Label1.Text = "File name: " & _
                      FileUpload1.PostedFile.FileName & "<br>" & _
                      "File Size: " & _
                      FileUpload1.PostedFile.ContentLength & " kb<br>" & _
                      "Content type: " & _
                      FileUpload1.PostedFile.ContentType
                Catch ex As Exception
                    Label1.Text = "ERROR: " & ex.Message.ToString()
                End Try
            Else
                Label1.Text = "Only .mp3 files allowed!"
            End If
        Else
            Label1.Text = "You have not specified a file."
        End If
    End Sub

C#

    protected void Button1_Click(object sender, EventArgs e)
    {

        if (FileUpload1.HasFile)
        {
            string fileExt = 
               System.IO.Path.GetExtension(FileUpload1.FileName);

            if (fileExt == ".mp3")
            {
                try
                {
                    FileUpload1.SaveAs("C:\\Uploads\\" + 
                       FileUpload1.FileName);
                    Label1.Text = "File name: " +
                        FileUpload1.PostedFile.FileName + "<br>" +
                        FileUpload1.PostedFile.ContentLength + " kb<br>" +
                        "Content type: " +
                        FileUpload1.PostedFile.ContentType;
                }
                catch (Exception ex)
                {
                    Label1.Text = "ERROR: " + ex.Message.ToString();
                }
            }
            else
            {
                Label1.Text = "Only .mp3 files allowed!";
            }
        }
        else
        {
            Label1.Text = "You have not specified a file.";
        }
    }

现在,通过使用 System.IO.Path 命名空间中的 GetExtension 方法,可以执行基本相同的操作。 请务必注意,这并不能绕过最终用户将文件扩展名更改为有效内容并将更改的文件上传到托管服务器的能力。

同时上传多个文件

到目前为止,你已看到一些关于如何毫不费力地将文件上传到服务器的良好示例。 现在,让我们看看如何从单个页面将多个文件上传到服务器。

Microsoft .NET Framework中没有内置功能,无法从单个 ASP.NET 页上传多个文件。 但是,只需一点点工作,即可像过去使用 .NET 1 一样轻松完成此任务。x.

技巧是将 System.IO 类导入 ASP.NET 页,然后使用 HttpFileCollection 类捕获随 Request 对象一起发送的所有文件。 此方法使你能够从单个页面上传任意数量的文件。

如果需要,只需单独处理页面上的每个 FileUpload 控件,如清单 5 所示。

列表 5. 单独处理每个 FileUpload 控件

Visual Basic

If FileUpload1.HasFile Then
   ' Handle file
End If

If FileUpload2.HasFile Then
   ' Handle file
End If

C#

if (FileUpload1.HasFile) {
   // Handle file
}

if (FileUpload2.HasFile) {
   // Handle file
}

此方法有效,但在某些情况下,你可能希望改用 HttpFileCollection 类处理文件,尤其是在使用动态生成的服务器控件列表时。

例如,可以使用按钮控件) 生成具有三个 FileUpload 控件和一个“提交”按钮 (ASP.NET 页。 用户单击“提交”按钮并将文件发布到服务器后,隐藏代码会获取文件并将其保存到服务器上的特定位置。 保存文件后,已发布的文件信息将显示在 ASP.NET 页中, (请参阅清单 6) 。

清单 6. 将多个文件上传到服务器

Visual Basic

Protected Sub Button1_Click(ByVal sender As Object, _
   ByVal e As System.EventArgs)

   Dim filepath As String = "C:\Uploads"
   Dim uploadedFiles As HttpFileCollection = Request.Files
   Dim i As Integer = 0

   Do Until i = uploadedFiles.Count
     Dim userPostedFile As HttpPostedFile = uploadedFiles(i)

     Try
        If (userPostedFile.ContentLength > 0) Then
           Label1.Text += "<u>File #" & (i + 1) & "</u><br>"
           Label1.Text += "File Content Type: " & _
              userPostedFile.ContentType & "<br>"
           Label1.Text += "File Size: " & _
              userPostedFile.ContentLength & "kb<br>"
           Label1.Text += "File Name: " & _
              userPostedFile.FileName & "<br>"

           userPostedFile.SaveAs(filepath & "\" & _
              System.IO.Path.GetFileName(userPostedFile.FileName))

           Label1.Text += "Location where saved: " & _
              filepath & "\" & _
              System.IO.Path.GetFileName(userPostedFile.FileName) & _
              "<p>"
        End If
     Catch ex As Exception
        Label1.Text += "Error:<br>" & ex.Message
     End Try
     i += 1
   Loop
End Sub

C#

protected void Button1_Click(object sender, EventArgs e)
{
   string filepath = "C:\\Uploads";
   HttpFileCollection uploadedFiles = Request.Files;
    
   for (int i = 0; i < uploadedFiles.Count; i++)
   {    
      HttpPostedFile userPostedFile = uploadedFiles[i];
    
      try
      {    
         if (userPostedFile.ContentLength > 0 )
         {
            Label1.Text += "<u>File #" + (i+1) + 
               "</u><br>";
            Label1.Text += "File Content Type: " + 
               userPostedFile.ContentType + "<br>";
            Label1.Text += "File Size: " + 
               userPostedFile.ContentLength + "kb<br>";
            Label1.Text += "File Name: " + 
               userPostedFile.FileName + "<br>";
    
            userPostedFile.SaveAs(filepath + "\\" + 
               System.IO.Path.GetFileName(userPostedFile.FileName));
    
            Label1.Text += "Location where saved: " + 
               filepath + "\\" + 
               System.IO.Path.GetFileName(userPostedFile.FileName) + 
               "<p>";
         }    
      } 
      catch (Exception Ex)
      {    
         Label1.Text += "Error: <br>" + Ex.Message;    
      }    
   }    
}

最终用户最多可以选择四个文件,然后单击“上传文件”按钮,这将初始化 Button1_Click 事件。 使用具有 Request.Files 属性的 HttpFileCollection 类可以控制从页面上传的所有文件。 当文件处于此状态时,可以使用它们执行任何操作。 在这种情况下,将检查文件的属性并将其写入屏幕。 最后,文件将保存到服务器的根目录中的 Uploads 文件夹中。 此操作的结果如图 5 所示。

Aa479405.uploadasp205 (en-us,MSDN.10) .gif

图 5。 从单个 ASP.NET 页一次将四个文件上传到服务器

你可能已经注意到,关于此示例的一个有趣的点是,文件输入文本框的状态不会随回发一起保存。 可以在图 5 中看到这一点。 在 ASP.NET 中,无法保存文件输入文本框的状态,因为这样做可能会带来安全风险。

结论

ASP.NET 提供的 FileUpload 服务器控件是一种功能强大的控件,在 Active Server Pages 3.0 时代很难实现。 此新功能允许最终用户将一个或多个文件上传到服务器。 请记住,可以通过使用 web.config.comments 或 web.config 文件中的设置来控制上传的文件的大小。

 

关于作者

Bill Evjen 是 .NET 技术和 .NET 基于社区的学习计划的积极支持者。 他是 路透社的技术总监,路透社是总部设在密苏里州圣路易斯的国际新闻和金融服务公司。 比尔是 国际 .NET 协会 (INETA) 的创始人兼执行董事,该协会代表全球 100,000 多名会员。 Bill 也是作者和演讲者,并撰写了诸如 ASP.NET Professional SecretsXML Web Services for ASP.NETWeb Services 增强Visual Basic .NET 圣经 (全部来自 Wiley) 的书籍。

© Microsoft Corporation. 保留所有权利。