使用 ASP.NET 和 SQL Server 上传和下载文件
本文介绍了一个在 ASP.NET 中使用 SQL Server 上传和下载文件的示例。
引言
本文介绍了一个在 ASP.NET 中使用 SQL Server 上传和下载文件的示例。
背景
与 MVC 相比,ASP.NET 现在被认为是“经典”的。但最近,我的一位老同事被分配了一个 Web 项目,他的开发环境是 ASP.NET。他让我提供一个在 ASP.NET 应用程序中使用 SQL Server 上传和下载文件的示例。由于我已经使用“MVC”一段时间了,手头没有 ASP.NET 的示例。我尝试“Google”一个可运行的示例,但找不到一个他可以简单下载并运行的,所以我创建了这个示例。通过意识到仍有大量经典的 ASP.NET 开发者,并且并非所有开发者都知道如何上传和下载文件,我决定在这里发布这个示例,为自己留下记录,并希望能帮助那些仍然对此主题感兴趣的人。
此示例的唯一重点是上传和下载文件,因此我没有关注面向对象设计、设计模式和编码标准。为简单起见,我甚至没有在代码中进行异常处理。我通过 ADO.NET 使用内联 SQL 访问数据库,但您绝对可以使用更高级的技术,例如 Entity Framework、Hibernate 和 存储过程,以利用这些技术可能提供的任何优势。同样为了简单起见,此示例未使用 JavaScript,因此我们可以专注于如何为感兴趣的人使用 SQL Server 在 ASP.NET 中上传和下载文件。
示例应用程序是在 Visual Studio 2010 和 SQL Server 2008 中开发的。如果您使用的是早期版本的 Visual Studio 和 SQL Server,您可能无法打开附加的解决方案。虽然我没有亲自测试,因为我自己没有早期版本,但我认为您可以在早期版本中简单地将代码复制并粘贴到您的应用程序中。我已尽力避免使用 Visual Studio 2010 和 SQL Server 2008 随附的“新”功能。
下图显示了附加的 Visual Studio 2010 解决方案
- “Default.aspx”页面是此应用程序的主要用户界面。它具有允许我们浏览和上传文件的控件。如果数据库中保存了文件,它将显示文件列表。它还为文件提供了超链接,以便我们可以下载它们。
- “GetFile.aspx”文件是一个空的 ASPX 页面。当我们点击 Default.aspx 页面上的下载超链接时,GetFile.aspx 页面的代码隐藏文件将加载数据库中保存的正确文件并将其发送到 Web 浏览器。
- “Utilities/FileUtilities.cs”文件实现了将文件保存到数据库和从数据库检索文件的数据库访问方法。
- 数据库连接字符串在“Web.config”文件中配置。
在本文中,我将首先介绍如何为该应用程序创建 SQL Server 数据库,然后向您展示如何在 ASP.NET 应用程序中上传和下载文件。
创建数据库
要创建数据库,您需要在 SQL Server 中拥有“管理员”权限。在我的环境中,我在本地计算机上安装了 SQL Server 2008。您可以运行以下脚本来创建数据库
USE [master]
GO
-- Create the [AFileStorageDB] database
IF EXISTS (SELECT name FROM sys.databases WHERE name = N'AFileStorageDB')
BEGIN
ALTER DATABASE [AFileStorageDB] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE [AFileStorageDB]
END
GO
CREATE DATABASE [AFileStorageDB]
GO
-- Create the FileLoader login to the server
IF EXISTS (SELECT * FROM sys.server_principals WHERE name = N'FileLoader')
DROP LOGIN [FileLoader]
GO
EXEC sp_addlogin @loginame = 'FileLoader', @passwd = 'Pd123456';
GO
USE [AFileStorageDB]
GO
-- Create the [Files] table to the [AFileStorageDB] database
CREATE TABLE [dbo].[Files](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[ContentType] [varchar](50) NOT NULL,
[Size] [bigint] NOT NULL,
[Data] [varbinary](max) NOT NULL,
CONSTRAINT [PK_Files] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
-- Grant the required database access to the login
sp_grantdbaccess 'FileLoader'
GO
sp_addrolemember 'db_datareader', 'FileLoader'
GO
sp_addrolemember 'db_datawriter', 'FileLoader'
GO
-- Bring the database on-line
ALTER DATABASE [AFileStorageDB] SET MULTI_USER
GO
此 SQL 脚本将以下内容添加到您的 SQL Server:
- 一个名为“AFileStorageDB”的数据库;
- 在“AFileStorageDB”数据库中一个名为“Files”的表;
- 一个名为“FileLoader”的 SQL Server 登录名,其密码为“Pd123456”;
- 该脚本还授予“FileLoader”登录名对“AFileStorageDB”数据库的“db_datareader”和“db_datawriter”访问权限。
上述脚本是“安全的”。但在运行它之前,请仔细检查您的服务器是否意外地已经有一个名为“AFileStorageDB”的数据库。如果存在,脚本将删除您的数据库并创建新的数据库,您可能会面临丢失所有数据的风险。脚本创建的数据库表“Files”如下图所示
到 AFileStorageDB 数据库的连接字符串保存在 ASP.NET 应用程序的“Web.config”文件中。
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime executionTimeout="240" maxRequestLength="20480" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<appSettings>
<add key="DBConnectionString"
value="Data Source=localhost;Initial Catalog=AFileStorageDB;
User Id=FileLoader;Password=Pd123456;pooling=false"/>
</appSettings>
</configuration>
数据库访问实用程序
“Utilities/FileUtilities.cs”文件实现了数据库访问实用程序类。
using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
namespace ASPNetFileUpDownLoad.Utilities
{
public class FileUtilities
{
private static string GetConnectionString()
{
return ConfigurationManager.AppSettings["DBConnectionString"];
}
private static void OpenConnection(SqlConnection connection)
{
connection.ConnectionString = GetConnectionString();
connection.Open();
}
// Get the list of the files in the database
public static DataTable GetFileList()
{
DataTable fileList = new DataTable();
using (SqlConnection connection = new SqlConnection())
{
OpenConnection(connection);
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandTimeout = 0;
cmd.CommandText = "SELECT ID, Name, ContentType, Size FROM Files";
cmd.CommandType = CommandType.Text;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
adapter.Fill(fileList);
connection.Close();
}
return fileList;
}
// Save a file into the database
public static void SaveFile(string name, string contentType,
int size, byte[] data)
{
using (SqlConnection connection = new SqlConnection())
{
OpenConnection(connection);
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandTimeout = 0;
string commandText = "INSERT INTO Files VALUES(@Name, @ContentType, ";
commandText = commandText + "@Size, @Data)";
cmd.CommandText = commandText;
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 100);
cmd.Parameters.Add("@ContentType", SqlDbType.VarChar, 50);
cmd.Parameters.Add("@size", SqlDbType.Int);
cmd.Parameters.Add("@Data", SqlDbType.VarBinary);
cmd.Parameters["@Name"].Value = name;
cmd.Parameters["@ContentType"].Value = contentType;
cmd.Parameters["@size"].Value = size;
cmd.Parameters["@Data"].Value = data;
cmd.ExecuteNonQuery();
connection.Close();
}
}
// Get a file from the database by ID
public static DataTable GetAFile(int id)
{
DataTable file = new DataTable();
using (SqlConnection connection = new SqlConnection())
{
OpenConnection(connection);
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandTimeout = 0;
cmd.CommandText = "SELECT ID, Name, ContentType, Size, Data FROM Files "
+ "WHERE ID=@ID";
cmd.CommandType = CommandType.Text;
SqlDataAdapter adapter = new SqlDataAdapter();
cmd.Parameters.Add("@ID", SqlDbType.Int);
cmd.Parameters["@ID"].Value = id;
adapter.SelectCommand = cmd;
adapter.Fill(file);
connection.Close();
}
return file;
}
}
}
此类实现了三个公共静态方法:
GetFileList
:检索数据库中保存的文件列表。SaveFile
:将文件保存到数据库。GetAFile
:检索单个文件,包括二进制文件数据,从数据库中。
这些方法是使用纯 ADO.NET 实现的。它们将由应用程序用于列出、上传和下载文件。当您检索所有文件列表时,请确保不要选择二进制文件数据,这会大大降低应用程序的速度。
“Default.aspx”页面
示例应用程序的主要用户界面实现在“Default.aspx”文件中。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
Inherits="ASPNetFileUpDownLoad.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>ASP.Net Up & Download Files</title>
<link href="Styles/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="frmDefault" enctype="multipart/form-data" runat="server">
<div style="width: 400px">
<div style="clear: both; width: 100%">
<input type="file" name="fileInput" />
<asp:Button ID="btnUpload" Text="Upload File" runat="server"
onclick="btnUpload_Click" />
</div>
<div style="margin-top: 5px; clear: both">
<asp:GridView ID="gvFiles" CssClass="GridViewStyle"
AutoGenerateColumns="true" runat="server">
<FooterStyle CssClass="GridViewFooterStyle" />
<RowStyle CssClass="GridViewRowStyle" />
<SelectedRowStyle CssClass="GridViewSelectedRowStyle" />
<PagerStyle CssClass="GridViewPagerStyle" />
<AlternatingRowStyle CssClass="GridViewAlternatingRowStyle" />
<HeaderStyle CssClass="GridViewHeaderStyle" />
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:HyperLink runat="server"
NavigateUrl='<%# Eval("ID", "GetFile.aspx?ID={0}") %>'
Text="Download"></asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</div>
</div>
</form>
</body>
</html>
在此“ASPX”页面中,我们有以下组件:
- 一个 HTML “
<input>
”类型为“file”。它用于浏览要上传的文件。 - 一个 ASP.NET
Button
。当用户点击此按钮时,所选文件将被上传到服务器。 - 一个 ASP.NET
GridView
。它用于显示已上传并保存在数据库中的所有文件的列表。
在 GridView
中,我添加了一个 HyperLink
列。超链接的 URL 指向 GetFile.aspx 页面,并附带正确的文件 ID 以下载相应的文件。
“Default.aspx”页面的代码隐藏文件实现如下:
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using ASPNetFileUpDownLoad.Utilities;
namespace ASPNetFileUpDownLoad
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (! IsPostBack)
{
DataTable fileList = FileUtilities.GetFileList();
gvFiles.DataSource = fileList;
gvFiles.DataBind();
}
}
protected void btnUpload_Click(object sender, EventArgs e)
{
// Although I put only one http file control on the aspx page,
// the following code can handle multiple file controls in a single aspx page.
HttpFileCollection files = Request.Files;
foreach (string fileTagName in files)
{
HttpPostedFile file = Request.Files[fileTagName];
if (file.ContentLength > 0)
{
// Due to the limit of the max for a int type, the largest file can be
// uploaded is 2147483647, which is very large anyway.
int size = file.ContentLength;
string name = file.FileName;
int position = name.LastIndexOf("\\");
name = name.Substring(position + 1);
string contentType = file.ContentType;
byte[] fileData = new byte[size];
file.InputStream.Read(fileData, 0, size);
FileUtilities.SaveFile(name, contentType, size, fileData);
}
}
DataTable fileList = FileUtilities.GetFileList();
gvFiles.DataSource = fileList;
gvFiles.DataBind();
}
}
}
- 在
Page_Load
事件中,如果不是回发,则从数据库检索所有文件列表并绑定到GridView
。 - 在
btnUpload_Click
事件中,如果上传了文件,则通过Request.Files
获取文件内容并将其保存到数据库。文件保存后,GridView
中保存的文件列表将被刷新。
“GetFile.aspx”页面
当文件列表显示在 Default.aspx 页面中时,每个文件都有一个相应的超链接来下载该文件。超链接指向 GetFile.aspx 页面,并附带正确的文件 ID。GetFile.aspx 页面本身是一个空的 ASPX 页面。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetFile.aspx.cs"
Inherits="ASPNetFileUpDownLoad.GetFile" %>
GetFile.aspx 页面可以向浏览器发送文件的技巧在于其代码隐藏文件。
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using ASPNetFileUpDownLoad.Utilities;
namespace ASPNetFileUpDownLoad
{
public partial class GetFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Get the file id from the query string
int id = Convert.ToInt16(Request.QueryString["ID"]);
// Get the file from the database
DataTable file = FileUtilities.GetAFile(id);
DataRow row = file.Rows[0];
string name = (string)row["Name"];
string contentType = (string)row["ContentType"];
Byte[] data = (Byte[])row["Data"];
// Send the file to the browser
Response.AddHeader("Content-type", contentType);
Response.AddHeader("Content-Disposition", "attachment; filename=" + name);
Response.BinaryWrite(data);
Response.Flush();
Response.End();
}
}
}
在 Page_Load
事件中,使用 QueryString
获取文件 ID。然后使用 FileUtilities
类中的 GetAFile
方法从数据库检索文件数据。通过 Response.BinaryWrite
将文件数据发送到浏览器。文件发送后,调用 Response.End
,它会立即终止服务器响应。这使得 GetFile.aspx 页面仅作为一个占位符,使浏览器能够访问在代码隐藏文件中实现的下载文件功能。
运行应用程序
现在我们已经完成了这个示例应用程序,可以对其进行测试了。将 Default.aspx 页面设置为起始页,然后我们可以启动应用程序。当 Default.aspx 页面首次加载时,我们可以在浏览器中看到“浏览”控件和“上传文件”按钮。
然后我们可以浏览一些文件并将它们上传到服务器。上传的文件将显示在 GridView
中。列表中的每个文件都有一个下载文件的超链接。
下图显示了从服务器下载的“Tiger.jpg”文件。
关注点
- 本文介绍了一个使用 ASP.NET 和 SQL Server 上传和下载文件的示例。
- 与 MVC 相比,ASP.NET 现在被认为是“经典”的,但仍有大量开发者使用 ASP.NET。
- 多年来,Web 应用程序中的文件上传技术得到了显著改进。有许多实用工具可以用来实现更好的用户体验。这里展示的方法是一种非常简单和基础的方法。它旨在让感兴趣的读者在 ASP.NET 环境中入门。
- 多年前,当我第一次学习使用 SQL Server 上传和下载文件时,我在一个网站上找到了与此方法相同的方法。但我再也找不到相同的链接了。可能是因为网站太老旧,或者网站不再活跃。我希望这个示例应用程序能帮助到那些仍然对此主题感兴趣的人。
- 虽然这个例子使用了 SQL Server,但相同的方法可以应用于任何支持二进制数据的数据库引擎。您只需要更改数据访问方法以适应特定的数据库引擎。
- 示例方法的一个优点是,文件从未保存在 Web 服务器上,因此此应用程序运行在 Web 服务器上不需要特殊权限。
- 希望您喜欢我的帖子,并希望本文能以某种方式帮助到您。
历史
- 2012/01/01:首次修订。