使用 ASP.NET 进行文件上传






4.65/5 (201投票s)
本文向您展示如何使用 ASP.NET 和 C# 进行文件上传。

回到 ASP 时代,通过网页上传文件是一个棘手的问题。问题在于,由于用于从客户端浏览器提交表单的加密类型,在服务器端接收此类文件是一项复杂的任务。数据必须以安全的字节数组形式检索并在解密后才能使用。大多数人求助于第三方 DLL。少数人编写了自己的 DLL,甚至有些人使用 VBScript 为此问题编写了纯 ASP 解决方案。幸运的是,.NET Framework SDK 附带了一些类,这些类从 Web 开发人员的角度使文件上传变得简单。本文将演示以下概念:
设置用于文件上传的 HTML 表单
HTML 表单能够上传文件的要求非常简单:您必须使用 multipart/form-data
加密,并且必须使用 post
方法。
<form id="Form1" method="post" runat="server" enctype="multipart/form-data">
用于上传文件的 HTML 输入控件的类型为 file
。
<input id="filMyFile" type="file" runat="server">
这是 HTML 表单需要完成的所有操作,以便客户端能够将文件提交到您的 ASP.NET 应用程序。
接收上传的文件
<INPUT TYPE="file"
对应于服务器端的 System.Web.UI.HtmlControls.HtmlInputFile
类型对象。因此,如果您像我一样,为页面使用 CodeBehind
架构,那么您的类中将有一个类似的成员字段定义:
protected HtmlInputFile filMyFile;
HtmlInputFile
类有几个不同的属性,但这里唯一真正关心的是 PostedFile
。此属性将告诉我们所有关于已上传到服务器的文件的信息。如果客户端未提交文件,则 PostedFile
属性为 null
。因此,我们可以通过执行以下操作来简单地检查是否发送了文件:
if( filMyFile.PostedFile != null )
{
// File was sent
}
else
{
// No file
}
如果文件确实已上传,则 PostedFile
属性将包含有效的 System.Web.HttpPostedFile
对象。HttpPostedFile
为我们提供了 4 个属性:
ContentLength
:上传文件的大小(字节)ContentType
:上传文件的 MIME 类型,即“image/gif”FileName
:客户端系统上上传文件的完整路径,即 c:\Some folder\MyPicture.gifInputStream
:流对象,使我们能够访问上传的数据
让我们找出文件的大小
// Get a reference to PostedFile object
HttpPostedFile myFile = filMyFile.PostedFile;
// Get size of uploaded file
int nFileLen = myFile.ContentLength;
既然我们知道了大小,就可以继续检索数据了。首先,我们需要分配一个字节数组来存储数据:
// Allocate a buffer for reading of the file
byte[] myData = new byte[nFileLen];
接下来,我们可以使用 InputStream
对象将上传的文件读入缓冲区
// Read uploaded file from the Stream
myFile.InputStream.Read(myData, 0, nFileLen);
此时,我们已成功将上传的文件检索到一个名为 myData
的字节数组中。接下来如何处理它在很大程度上取决于您的应用程序的要求。我将在接下来的部分中展示将文件保存到硬盘和数据库。
将上传的文件保存到磁盘
我为我的演示项目编写了一个简单的函数,用于将文件存储到磁盘:
// Writes file to current folder
private void WriteToFile(string strPath, ref byte[] Buffer)
{
// Create a file
FileStream newFile = new FileStream(strPath, FileMode.Create);
// Write data to the file
newFile.Write(Buffer, 0, Buffer.Length);
// Close file
newFile.Close();
}
我将文件要存储的完整路径和包含文件数据的缓冲区的引用传递给此函数。它使用 System.IO.FileStream
对象将缓冲区写入磁盘。这种非常简单的三行代码方法在大多数情况下都适用。这里有几个注意事项:获取上传文件的文件名和安全性。由于 PostedFile
的 FileName
属性是客户端计算机上上传文件的完整路径,我们可能只希望使用该路径的文件名部分。我们可以使用一个非常方便的小工具类:System.IO.Path
,而不是使用一些解析技术来查找反斜杠等。
string strFilename = Path.GetFileName(myFile.FileName);
安全是另一个问题。在我的演示项目中,我将文件存储在项目执行的同一文件夹中。为了使其正常工作,ASPNET 帐户(用于执行 ASP.NET 进程的帐户)必须对该文件夹具有写入权限。默认情况下没有,因此您需要右键单击该文件夹,转到“安全”选项卡,并将 ASP.NET 帐户添加到列表中。然后通过选中“写入”复选框并单击“确定”来授予该帐户写入权限。
将上传的文件保存到数据库
以下函数在我的演示项目中用于将上传的文件存储到数据库中:
// Writes file to the database
private int WriteToDB(string strName, string strType, ref byte[] Buffer)
{
int nFileID = 0;
// Create connection
OleDbConnection dbConn = new OleDbConnection(GetConnectionString());
// Create Adapter
OleDbDataAdapter dbAdapt = new OleDbDataAdapter("SELECT * FROM tblFile", dbConn);
// We need this to get an ID back from the database
dbAdapt.MissingSchemaAction = MissingSchemaAction.AddWithKey;
// Create and initialize CommandBuilder
OleDbCommandBuilder dbCB = new OleDbCommandBuilder(dbAdapt);
// Open Connection
dbConn.Open();
// New DataSet
DataSet dbSet = new DataSet();
// Populate DataSet with data
dbAdapt.Fill(dbSet, "tblFile");
// Get reference to our table
DataTable dbTable = dbSet.Tables["tblFile"];
// Create new row
DataRow dbRow = dbTable.NewRow();
// Store data in the row
dbRow["FileName"] = strName;
dbRow["FileSize"] = Buffer.Length;
dbRow["ContentType"] = strType;
dbRow["FileData"] = Buffer;
// Add row back to table
dbTable.Rows.Add(dbRow);
// Update data source
dbAdapt.Update(dbSet, "tblFile");
// Get newFileID
if( !dbRow.IsNull("FileID") )
nFileID = (int)dbRow["FileID"];
// Close connection
dbConn.Close();
// Return FileID
return nFileID;
}
我在演示项目中使用了一个 Access 数据库,它只有一个表 tblFile
,其中包含 5 个字段,定义如下:
FileID – 自动编号 Filename – 文本 255 FileSize - 长整型 ContentType – 文本 100 FileData – OLE 对象
上述函数中的代码非常直观。我创建了 OleDbConnection
、OleDbDataAdapter
和 DataSet
对象,然后将一行添加到 DataSet
中唯一的表中。这里需要注意的重要一点是,需要创建 OleDbCommandBuilder
对象并使用对 OleDbDataAdapter
对象的引用进行初始化,以便自动为我们构建插入查询。此外,如果您想检索刚刚存储在数据库中的文件的新分配的 FileID,您需要确保将 Adapter 的 MissingSchemaAction
属性设置为 MissingSchemaAction.AddWithKey
。这确保了当您调用 DataAdapter
的 Update
方法时,主键/自动编号 FileID 字段将使用新的 ID 填充。
本地 ASP.NET 帐户必须对数据库文件具有写入权限,您的代码才能成功。如果您的文件放置在 wwwroot
下的任何位置,您需要在运行我的演示项目之前手动为数据库文件授予所有必要的权限。
从数据库中检索文件
我在我的演示项目中使用了相同的 ASPX 页面来读取和返回数据库中的文件数据。我检查了传递给页面的 FileID 参数。如果它已传递,我知道我需要返回一个文件而不是处理用户的输入:
private void Page_Load(object sender, System.EventArgs e)
{
// Check if FileID was passed to this page as a parameter
if( Request.QueryString["FileID"] != null )
{
// Get the file out of database and return it to requesting client
ShowTheFile(Convert.ToInt32(Request.QueryString["FileID"]));
}
}
ShowTheFile
函数在这里完成了所有工作:
// Read file out of the database and returns it to client
private void ShowTheFile(int FileID)
{
// Define SQL select statement
string SQL = "SELECT FileSize, FileData, ContentType FROM tblFile WHERE FileID = "
+ FileID.ToString();
// Create Connection object
OleDbConnection dbConn = new OleDbConnection(GetConnectionString());
// Create Command Object
OleDbCommand dbComm = new OleDbCommand(SQL, dbConn);
// Open Connection
dbConn.Open();
// Execute command and receive DataReader
OleDbDataReader dbRead = dbComm.ExecuteReader();
// Read row
dbRead.Read();
// Clear Response buffer
Response.Clear();
// Set ContentType to the ContentType of our file
Response.ContentType = (string)dbRead["ContentType"];
// Write data out of database into Output Stream
Response.OutputStream.Write((byte[])dbRead["FileData"], 0, (int)dbRead["FileSize"]);
// Close database connection
dbConn.Close();
// End the page
Response.End();
}
我使用 OleDbConnection
、OleDbCommand
和 OleDbDataReader
对象来检索数据。下一步是清除 Response
输出缓冲区,以确保除了文件数据之外没有其他信息发送给客户端。这仅在您的页面缓冲已打开(ASP.NET 中的默认设置)的情况下才有效。我将 Response
对象的 ContentType
属性设置为文件的内容类型。然后我将文件数据写入 Response
对象的 Output 流中。这里重要的是在最后调用 Response.End()
以防止该页面的进一步处理。我们在这里完成了,我们不希望其他代码执行。
安全注意事项
重要
由于我的演示项目将文件写入磁盘或数据写入 Access 数据库,因此您需要确保在您要写入的文件夹和文件上正确设置了安全权限。.NET 的 Beta 版本使用 System
帐户执行 ASP.NET 进程,并且它可以访问计算机上的所有内容。然而,.NET 的最终版本在本地 ASP.NET 帐户下运行所有 ASP.NET 进程。该帐户需要对用于写入数据的所有文件夹和文件具有写入权限。
为了使我的演示项目在 .NET 发布版本下工作,您需要将其放置在一个文件夹中(wwwroot
下的任何位置),并按如下方式设置该文件夹的权限:右键单击该文件夹,转到“属性”、“安全”选项卡,确保本地 ASPNET 帐户对该文件夹具有读取和写入权限。如果没有,请将本地 ASPNET 帐户添加到列表中,并授予其读取和写入权限。单击“确定”。