65.9K
CodeProject 正在变化。 阅读更多。
Home

一个用于上传和下载文件的 ASP.NET MVC Web 项目示例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (21投票s)

2010年5月4日

CPOL

5分钟阅读

viewsIcon

252010

downloadIcon

26120

本文介绍了一个用于上传和下载文件的 ASP.NET MVC Web 项目示例。

引言

本文介绍了一个用于上传和下载文件的 ASP.NET MVC Web 项目示例。

背景

这是一篇简短的文章,介绍了一个用于上传和下载文件的 ASP.NET MVC Web 项目示例。如果您想了解 ASP.NET MVC 的工作原理,可以参考我之前的文章《使用 MVC 模式开发 ASP.Net 应用程序的简单教程》。本项目在 Visual Studio 2008 中开发。

解决方案资源管理器中的项目

示例项目中重要的文件显示在解决方案资源管理器中

ASPNetMVCDemo/SolutionExplorer.jpg

我们首先看一下添加到根文件夹“Web.config”文件中的应用程序配置信息。之后,我们将看一个小型实用类,然后深入了解该 ASP.NET MVC 项目的模型、视图和控制器。

Web 应用程序的配置

此 Web 项目有两个“web.config”文件。位于“Views”文件夹中的那个是 MVC 特有的。它用于阻止直接访问“Views”文件夹中实现的“aspx”视图。应用程序的配置信息应写入根文件夹中的“Web.config”文件。添加到应用程序的配置信息如下:

<appSettings>
    <add key="ApplicationName"
	value="An ASP.NET File Upload Example Project in MVC Pattern"/>
    <add key="Author" value="Song Li"/>
    <add key="DevelopmentTime" value="5/4/2010"/>
    <add key="DeploymentVirtualDirectory" value=""/>
</appSettings>

在配置信息中,“DeploymentVirtualDirectory”在开发时需要设置为空字符串。但在部署时需要正确的虚拟目录名。应用程序将通过“ApplicationSettings.cs”文件(位于“Models”文件夹中)中实现的模型类“ApplicationSettings”之一来使用配置信息。

using System;
using System.Configuration;

namespace ASPNetMVCDemo.Models
{
    public class ApplicationSettings
    {
        private static object _threadLock = new Object();
        private static ApplicationSettings _Instance = null;

        private string _ApplicationName;
        private string _Author;
        private string _DevelopmentTime;
        private string _DeploymentVirtualDirectory;

        public string ApplicationName { get { return _ApplicationName; } }
        public string Author { get { return _Author; } }
        public string DevelopmentTime { get { return _DevelopmentTime; } }
        public string DeploymentVirtualDirectory {
            get { return _DeploymentVirtualDirectory; } }

        private ApplicationSettings()
        {
            _ApplicationName = ConfigurationManager.AppSettings["ApplicationName"];
            _Author = ConfigurationManager.AppSettings["Author"];
            _DevelopmentTime = ConfigurationManager.AppSettings["DevelopmentTime"];
            _DeploymentVirtualDirectory
                = ConfigurationManager.AppSettings["DeploymentVirtualDirectory"];
        }

        public static ApplicationSettings GetInstance()
        {
            lock (_threadLock)
                if (_Instance == null)
                    _Instance = new ApplicationSettings();

            return _Instance;
        }
    }
}

这是一个线程安全的单例类,用于从“Web.config”文件中读取配置信息。应用程序可以通过此类公共属性访问应用程序配置信息。

一个实用类

为了简化后续开发,在“Utilities”文件夹的“ApplicationUtility.cs”文件中实现了一个static实用类“ApplicationUtility”。

using System;
using System.Text;
using ASPNetMVCDemo.Models;

namespace ASPNetMVCDemo.Utilities
{
    public static class ApplicationUtility
    {
        public static string FormatURL(string PathWithoutVirtualDirectoryName)
        {
            ApplicationSettings appInfomation
                = ApplicationSettings.GetInstance();
            string DeploymentVirtualDirectory
                = appInfomation.DeploymentVirtualDirectory;

            if (DeploymentVirtualDirectory == "")
            {
                return PathWithoutVirtualDirectoryName;
            }

            StringBuilder SB = new StringBuilder();
            SB.Append("/");
            SB.Append(appInfomation.DeploymentVirtualDirectory);
            SB.Append("/");
            SB.Append(PathWithoutVirtualDirectoryName);

            return SB.ToString();
        }

        public static string jQueryLink()
        {
            StringBuilder SB = new StringBuilder();
            SB.Append("<script src=\"");
            SB.Append(ApplicationUtility.FormatURL("/Scripts/jquery-1.4.1.min.js"));
            SB.Append("\" type=\"text/javascript\"></script>");

            return SB.ToString();
        }

        public static string AppStylelink()
        {
           StringBuilder SB = new StringBuilder();
            SB.Append("<link href=\"");
            SB.Append(ApplicationUtility.FormatURL("/Styles/AppStyles.css"));
            SB.Append("\" rel=\"stylesheet\" type=\"text/css\" />");

            return SB.ToString();
        }
    }
}

该类公开了三个public方法来格式化 URL。其中两个用于创建 CSS 文件链接和 jQuery 脚本库链接。为了格式化 URL,需要在根目录中的“Web.config”文件中配置正确的虚拟目录名。在开发时,此值需要为空字符串。

模型

位于“Models”文件夹的“ExistingFiles.cs”文件中实现的“ExistingFilesModel”类代表了 MVC 应用程序的模型。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Web;

namespace ASPNetMVCDemo.Models
{
    public class ExistingFilesModel
    {
        private DataTable UploadedFiles;

        public ExistingFilesModel()
        {
            UploadedFiles = new DataTable();
            DataColumn IDColumn = UploadedFiles.Columns.Add
				("ID", Type.GetType("System.Int32"));
            IDColumn.AutoIncrement = true;
            IDColumn.AutoIncrementSeed = 1;
            IDColumn.AutoIncrementStep = 1;
            DataColumn[] keys = new DataColumn[1];
            keys[0] = IDColumn;
            UploadedFiles.PrimaryKey = keys;

            UploadedFiles.Columns.Add("File Name", Type.GetType("System.String"));
            UploadedFiles.Columns.Add("File Size", Type.GetType("System.Int32"));
            UploadedFiles.Columns.Add("Context Type", Type.GetType("System.String"));
            UploadedFiles.Columns.Add("Time Uploadeded", Type.GetType("System.DateTime"));
            UploadedFiles.Columns.Add("File Data", Type.GetType("System.Byte[]"));
        }

        public DataTable GetUploadedFiles() { return UploadedFiles; }

        public void AddAFile(string FileName,
		int Size, string ContentType, Byte[] FileData)
        {
            DataRow row = UploadedFiles.NewRow();
            UploadedFiles.Rows.Add(row);

            row["File Name"] = FileName;
            row["File Size"] = Size;
            row["Context Type"] = ContentType;
            row["Time Uploadeded"] = System.DateTime.Now;
            row["File Data"] = FileData;
        }

        public void DeleteAFile(int ID)
        {
            DataRow RowToDelete = UploadedFiles.Rows.Find(ID);
            if (RowToDelete != null)
            {
                UploadedFiles.Rows.Remove(RowToDelete);
            }
        }
    }
}

此类使用“DataTable”存储应用程序数据,即已上传文件的列表。它还公开了用于添加文件、删除文件和检索文件列表的public方法。此类的一个实例将保存在应用程序的 Web 会话中。

View

ASP.NET MVC 应用程序默认会使用主母版页。为简单起见,本应用程序选择不使用它。“Views\FileUploadExample”文件夹中实现的“FileUpload.aspx”视图是本应用程序主要的职能视图。

<%@ Page Language="C#"
    CodeBehind="~/Views/FileUploadExample/FileUpload.aspx.cs"
    AutoEventWireup="true" EnableViewState="false"
    Inherits="ASPNetMVCDemo.Views.FileUploadExample.FileUpload" %>

<!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>File Upload Example by ASP.NET in MVC Pattern</title>
    <asp:Literal ID="litLinkTojQuery" runat="server" />
    <asp:Literal ID="litAppStyle" runat="server" />
    <script language="javascript" type="text/javascript">
        $(document).ready(function() {
            $("a").hover(function() {
                this.style.color = "red";
            }, function() {
                this.style.color = "";
            });

            $.mouseX = function(e) {
                if (e.pageX) return e.pageX;
                else if (e.clientX)
                    return e.clientX + (document.documentElement.scrollLeft ?
                    document.documentElement.scrollLeft :
                    document.body.scrollLeft);
                else return null;
            };

            $.mouseY = function(e) {
                if (e.pageY) return e.pageY;
                else if (e.clientY)
                    return e.clientY + (document.documentElement.scrollTop ?
                    document.documentElement.scrollTop :
                    document.body.scrollTop);
                else return null;
            };

            $("#spanStartUpload").hover(function() {
                this.style.color = "red";
            }, function() {
                this.style.color = "blue";
            });

            $("#spanStartUpload").click(function(e) {
                e.preventDefault();
                $("#divFileUpload").slideToggle("fast");
                $file = $("#spanFileUpload");
                $file.html($file.html());
            });

            $("#btnCancel").click(function(e) {
                $file = $("#spanFileUpload");
                $file.html($file.html());
                $("#divFileUpload").css("display", "none");
            });

            $("#btnUpload").click(function(e) {
                $file = $("#FileToLoad");
                var $filePath = $.trim($file.val());
                if ($filePath == "") {
                    alert("Please browse a file to upload");
                    return;
                }

                var $ext = $filePath.split(".").pop().toLowerCase();
                var $allow = new Array("gif", "png", "jpg", "jpeg");
                if ($.inArray($ext, $allow) == -1) {
                    alert("Only image files are accepted, please browse a image file");
                    return;
                }

                var $uploadaction = $("#hidFileUploadAction").val();
                $("#MainForm").attr("action", $uploadaction);

                try {
                    $("#MainForm").submit();
                } catch (err) {
                    alert("Upload file failed, make sure to browse a valid file path,
			and make sure the file is not openned by any programs.");
                }

            });

            $(".ImagePopLink").click(function(e) {
                e.preventDefault();
                var $ID = $(this).html();
                var $imagebase = $("#hidImageBase").val();
                var $imagesource = $imagebase + "?ID=" + $ID;
                var $imagelink = "<img style=\"width: 400px\" src=\""
				+ $imagesource + "\" />";
                $("#divImg").html($imagelink);
                $("#divImg").load();
                $("#PopWindow").css({ "left": $.mouseX(e), "top": $.mouseY(e) + 5 });
                $("#PopWindow").show();
            });

            $("#Close").click(function(e) {
                e.preventDefault();
                $("#PopWindow").css({ "left": "0px", "top": "0px" });
                $("#PopWindow").hide();
            });

        });
    </script>
</head>

<body class="DocumentDefault">
<input type="hidden" id="hidFileUploadAction" runat="server" />
<input type="hidden" id="hidImageBase" runat="server" />
<form name="MainForm" method="post" action="" id="MainForm"
    enctype="multipart/form-data" runat="server">

    <div class="DocumentTitle">
        <asp:Literal id="litApplicationName" runat="server" />
    </div>

    <div class="DocumentAuthor">
        <asp:Literal id="litAuthorInformation" runat="server" />
    </div>

    <div style="text-align: left">
        <span id="spanStartUpload" class="BoldLink">
            Click to upload a new file</span>
    </div>

    <div id="divFileUpload" style="display: none; text-align: left">
        <span style="font-weight: bold">Please browse a file to upload&nbsp;</span>
        <span id="spanFileUpload">
            <input type="file" name="FileToLoad" id="FileToLoad" style="width:350px;" />
        </span>
        <span>
            <input type="button" id="btnUpload" value="Submit"
			style="width: 75px; height: 21px" />
        </span>
        <span>
            <input type="button" id="btnCancel" value="Cancel"
			style="width: 75px; height: 21px" />
        </span>
    </div>

<div id="MainContent" class="MainContent">
    <asp:Literal ID="litExistingFiles" runat="server" />
</div>
<div class="Copyright">Copy right: The Code Project Open License (CPOL)</div>

<div id="PopWindow" class="HiddenPopup">
<div>
    <div style="background-color:
		Transparent; position:absolute; top: 1px; left: 380px">
        <a id="Close" style="font-weight: bold" href="#">X</a></div>
    <div id="divImg"></div>
</div>
</div>

</div>
</form>
</body>
</html>

此视图的代码隐藏文件实现如下:

using System;
using System.Text;
using System.Data;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using ASPNetMVCDemo.Utilities;

namespace ASPNetMVCDemo.Views.FileUploadExample
{
    public class FileUpload : System.Web.Mvc.ViewPage
    {
        protected Literal litApplicationName;
        protected Literal litAuthorInformation;
        protected Literal litExistingFiles;
        protected Literal litLinkTojQuery;
        protected Literal litAppStyle;
        protected HtmlInputHidden hidFileUploadAction;
        protected HtmlInputHidden hidImageBase;

        protected void Page_Load(object sender, EventArgs e)
        {
            Response.Cache.SetCacheability(HttpCacheability.NoCache);

            Models.ApplicationSettings appsettings =
			Models.ApplicationSettings.GetInstance();

            litApplicationName.Text = appsettings.ApplicationName;
            hidFileUploadAction.Value
                = ApplicationUtility.FormatURL("/FileUploadExample/SaveFile");
            hidImageBase.Value = ApplicationUtility.FormatURL
				("/FileUploadExample/GetAFile");

            litLinkTojQuery.Text = ApplicationUtility.jQueryLink();
            litAppStyle.Text = ApplicationUtility.AppStylelink();

            StringBuilder SB = new StringBuilder();
            SB.Append("Developed by ");
            SB.Append(appsettings.Author);
            SB.Append(" on ");
            SB.Append(appsettings.DevelopmentTime);
            litAuthorInformation.Text = SB.ToString();

            SB.Remove(0, SB.Length);
            DataView FileView = (DataView)ViewData["ExistingFileList"];
            SB.Append("<table style=\"width: 99%;\" ");
            SB.Append("rules=\"all\" border=\"1px\" ");
            SB.Append("cellspacing=\"0px\" cellpadding=\"4px\">");

            SB.Append("<tr style=\"background-color: Silver; color: white; ");
            SB.Append("font-weight: bold\">");
            foreach (DataColumn aColumn in FileView.Table.Columns)
            {
                if (aColumn.ColumnMapping == MappingType.Hidden)
                {
                    continue;
                }

                SB.Append("<td>");
                SB.Append(aColumn.ColumnName);
                SB.Append("</td>");
            }
            SB.Append("<td>&nbsp;</td>");
            SB.Append("<td>&nbsp;</td>");
            SB.Append("</tr>");

            foreach (DataRowView aRowView in FileView)
            {
                SB.Append("<tr>");
                foreach (DataColumn aColumn in FileView.Table.Columns)
                {
                    if (aColumn.ColumnMapping == MappingType.Hidden)
                    {
                        continue;
                    }

                    SB.Append("<td>");
                    if (aColumn.ColumnName == "ID")
                    {
                        SB.Append("<span class=\"ImagePopLink\">");
                        SB.Append(aRowView[aColumn.ColumnName].ToString());
                        SB.Append("</span>");
                    }
                    else
                    {
                        SB.Append(aRowView[aColumn.ColumnName].ToString());
                    }

                    SB.Append("</td>");
                }

                string ID = aRowView["ID"].ToString();
                SB.Append("<td>");
                SB.Append("<a href=\"");
                SB.Append(ApplicationUtility.FormatURL("/FileUploadExample/GetAFile"));
                SB.Append("?ATTACH=YES&ID=");
                SB.Append(ID);
                SB.Append("\">Download this file</a>");
                SB.Append("</td>");

                SB.Append("<td>");
                SB.Append("<a href=\"");
                SB.Append(ApplicationUtility.FormatURL("/FileUploadExample/DeleteFile"));
                SB.Append("?ID=");
                SB.Append(ID);
                SB.Append("\">Delete this file</a>");
                SB.Append("</td>");

                SB.Append("</tr>");
            }

            SB.Append("</table>");

            litExistingFiles.Text = SB.ToString();
        }
    }
}

关于是否应该在视图中使用代码隐藏文件存在激烈的讨论。如果我们不使用代码隐藏文件,则需要在视图中使用内联服务器脚本。无论是否使用代码隐藏文件,模型类中的应用程序数据都不应在视图中进行修改,以更好地遵循 MVC 方法。

控制器

此简单应用程序的控制器实现在“Controllers”文件夹的“FileUploadExampleController.cs”文件中的“FileUploadExampleController”类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Web;
using System.IO;
using System.Web.Mvc;
using ASPNetMVCDemo.Models;

namespace ASPNetMVCDemo.Controllers
{
    public class FileUploadExampleController : Controller
    {
        private ExistingFilesModel GetModelFromSession()
        {
            ExistingFilesModel theModel =
		(ExistingFilesModel)Session["ExistingFilesModel"];
            if (theModel == null)
            {
                theModel = new ExistingFilesModel();
                DataTable aTable = theModel.GetUploadedFiles();
                aTable.Columns["File Data"].ColumnMapping = MappingType.Hidden;

                Session["ExistingFilesModel"] = theModel;
            }

            return theModel;
        }

        public ActionResult FileUpload()
        {
            ExistingFilesModel theModel = GetModelFromSession();
            DataTable aTable = theModel.GetUploadedFiles();

            ViewData["ExistingFileList"] = aTable.DefaultView;
            return View();
        }

        public ActionResult DeleteFile()
        {
            string ID = Request.QueryString["ID"];
            int intID;

            if (!Int32.TryParse(ID, out intID))
            {
                ViewData["ERROR"] = "Please provide a valid student ID";
                return View("../Shared/Error");
            }

            ExistingFilesModel theModel = GetModelFromSession();
            theModel.DeleteAFile(intID);

            return RedirectToAction("FileUpload");
        }

        public ActionResult SaveFile()
        {
            ExistingFilesModel theModel = GetModelFromSession();
            HttpPostedFileBase File = Request.Files["FileToLoad"];

            if (File != null)
            {
                int Size = File.ContentLength;

                if (Size <= 0)
                {
                    ViewData["ERROR"] = "You uploaded an empty file,
			please browse a valid file to upload";
                    return View("../Shared/Error");
                }

                string FileName = File.FileName;
                int Position = FileName.LastIndexOf("\\");
                FileName = FileName.Substring(Position + 1);
                string ContentType = File.ContentType;
                byte[] FileData = new byte[Size];
                File.InputStream.Read(FileData, 0, Size);

                theModel.AddAFile(FileName, Size, ContentType, FileData);
            }

            return RedirectToAction("FileUpload");
        }

        public ActionResult GetAFile()
        {
            string Attachment = Request.QueryString["ATTACH"];
            string ID = Request.QueryString["ID"];
            int intID;

            if (!Int32.TryParse(ID, out intID))
            {
                ViewData["ERROR"] = "Please provide a valid student ID";
                return View("../Shared/Error");
            }

            ExistingFilesModel theModel = GetModelFromSession();
            DataTable aTable = theModel.GetUploadedFiles();
            DataRow FileRow = aTable.Rows.Find(intID);
            if (FileRow == null)
            {
                ViewData["ERROR"] = "Please provide a valid student ID";
                return View("../Shared/Error");
            }

            string FileName = (string) FileRow["File Name"];
            int Size = (int) FileRow["File Size"];
            string ContentType = (string) FileRow["Context Type"];
            Byte[] Data = (Byte[]) FileRow["File Data"];


            Response.ContentType = ContentType;
            StringBuilder SB = new StringBuilder();
            if (Attachment == "YES")
            {
                SB.Append("attachment; ");
            }
            SB.Append("filename=");
            SB.Append(FileName);

            Response.AddHeader("Content-Disposition", SB.ToString());
            Response.BinaryWrite(Data);
            Response.Flush();
            Response.End();

            return new EmptyResult();
        }
    }
}

此类控制器执行以下任务:

  • 从 Web 会话中检索上传的文件
  • 保存新文件
  • 删除现有文件
  • 将文件数据发送到 Web 浏览器进行下载

如果在处理任务时遇到错误,操作方法将选择“View\Shared”文件夹中实现的“Error.aspx”视图向用户显示错误消息。

运行应用程序

现在我们完成了此示例应用程序的开发。在 Visual Studio 中,我们可以按“F5”进行调试运行应用程序。单击“Click to upload a new file”(点击上传新文件)链接,应用程序将显示文件上传控件,允许用户浏览文件以上传到服务器。上传几个图片文件后,应用程序看起来像这样:

ASPNetMVCDemo/RunApp.jpg

单击“Delete this file”(删除此文件)链接,您可以删除选定的文件。单击文件的“ID”,图像将在弹出窗口中显示

ASPNetMVCDemo/RunAppShowPicture.jpg

单击“Download this file”(下载此文件)链接,您将被允许下载选定的文件

ASPNetMVCDemo/RunAppDownload.jpg

单击“Open”(打开)按钮,图像文件将被下载并显示如下:

ASPNetMVCDemo/RunAppDownloadResult.jpg

关注点

  • 有许多方法可以向 Web 服务器上传和下载文件。本文仅展示了如何在 ASP.NET MVC 应用程序的 MVC 控制器之一中完成此操作。
  • 关于是否应该在 ASP.NET MVC 应用程序的视图中使用代码隐藏文件存在激烈的讨论。如果我们不使用代码隐藏文件,则需要在视图中使用内联服务器脚本。
  • 上传到服务器的文件保存在 Web 会话的“DataTable”中,因此 Visual Studio 是开发和调试应用程序所需的唯一工具。在任何实际的 Web 应用程序中,都需要持久化的数据存储来保存文件。
  • 出于演示目的,文件上传和下载功能被构建到一个独立的 Web 应用程序中,在更全面的 IT 应用程序中,这些功能更有可能构建在更大应用程序的子系统中。
  • 为了更方便地编写客户端 JavaScript 程序,使用了 jQuery 库。借助 jQuery 库,浏览器兼容性问题不再是主要问题。我已在“Internet Explorer”、“FireFox”和“Google Chrome”中测试了此应用程序。该应用程序在这三个主要浏览器中都能正常运行。
  • 此应用程序使用实用类“ApplicationUtility”来解决与 ASP.NET MVC 应用程序相关的“相对路径问题”。“FormatURL”方法用于利用“Web.config”文件中配置的“DeploymentVirtualDirectory”信息,以确保无论应用程序部署在哪里都能生成正确的 URL。调试运行应用程序时,“DeploymentVirtualDirectory”需要设置为空字符串。在部署时,需要将正确的虚拟目录名写入“Web.config”文件。

历史

这是本文的第一个修订版。

© . All rights reserved.