1. 程式人生 > >基於ASP.NET MVC 4、WebApi、jQuery和FormData的多檔案上傳方法

基於ASP.NET MVC 4、WebApi、jQuery和FormData的多檔案上傳方法

通過<input type='file' />上傳檔案是網站應用系統的一個經典應用,可參考的文章較多。因為多種原因,筆者不能遠端桌面連線伺服器,只有通過網站方式上傳更新的應用系統檔案。正好五一幾天,把原來的有關構思程式設計實現,即鞏固了所學的ASP.NET MVC WebApi知識,也做一個通用的檔案上傳網站。本文不打算介紹實際的通用網站,而是用一個簡單例項主要介紹相關技術。

1、構建和初始化路由

控制器採用預設路由,WebApi採用定製路由,見如下全域性檔案Global、預設過濾器配置、預設控制器路由配置和定製WebApi路由配置程式的程式碼:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

namespace CSUST.Files
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()  // 全域性檔案 Global.asax.cs
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            this.SetJsonFormatter();
        }

        private void SetJsonFormatter()
        {
            GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
            System.Net.Http.Headers.MediaTypeHeaderValue jsonFormatter = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Add(jsonFormatter);  // 必須
        }
    }
}
using System.Web;
using System.Web.Mvc;

namespace CSUST.Files
{
    public class FilterConfig  // 過濾器配置 FilterConfig.cs
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
    }
}
using System.Web.Mvc;
using System.Web.Routing;

namespace CSUST.Files
{
    public class RouteConfig  // 預設控制器路由配置 RouteConfig.cs
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}
using System.Web.Http;

namespace CSUST.Files
{
    public static class WebApiConfig  // 定製WebApi路由配置 WebApiConfig.cs
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Routes.MapHttpRoute(
                name: "DefaultApi2",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { action = RouteParameter.Optional, id = RouteParameter.Optional }
            );
        }
    }
}
2、Home控制器程式碼和WebApi控制器程式碼

如下是預設的控制器HomeController程式碼以及多檔案上傳處理的WebApi程式碼

using System.Web.Mvc;

namespace CSUST.Files
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}
using System;
using System.Web;
using System.Web.Http;


namespace CSUST.Files
{
    public class FilesApiController : ApiController  // FilesApi控制器
    {
        [HttpPost]
        public string Upload()
        {
            try
            {
                var httpRequest = HttpContext.Current.Request;
                var dirName = httpRequest.Form["DirName"];  // 獲取 FormData的鍵值


                System.Text.StringBuilder ss = new System.Text.StringBuilder();
                ss.Append("成功上傳檔案" + Environment.NewLine);


                foreach (string key in httpRequest.Files)  // 檔案鍵
                {
                    var postedFile = httpRequest.Files[key];    // 獲取檔案鍵對應的檔案物件
                    var file = dirName + postedFile.FileName;
                    postedFile.SaveAs(file);
                    ss.Append(file + Environment.NewLine);
                }


                return ss.ToString();
            }
            catch (Exception err)
            {
                return err.Message;
            }
        }
    }
}
3、檢視檔案Index.cshtml

下面是HomeController控制器Index方法對應的檢視檔案。

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <title>檔案上傳</title>
<script type="text/javascript" src='@Url.Action("jquery-1.12.4.min.js", "scripts")'></script>

<script type="text/javascript">
    function Upload()
    {
        var dir = $("#cbDirNames").val();
        var f1 = $("#tbFileName1").val();
        var f2 = $("#tbFileName2").val();
        
        if ((f1 == null || f1 == "") && (f2 == null || f2 == ""))
        {
            alert("至少要上傳一個檔案。");
            return;
        }

        var formData = new FormData();
        formData.append("DirName", dir);

        if (f1 != null && f1 != "")
        {
            var files = $("#tbFileName1").get(0).files;
            if (files.length > 0)
            {
                formData.append("File1", files[0]);  // Add the uploaded image content to the form data collection
            }
        }

        if (f2 != null && f2 != "")
        {
            var files = $("#tbFileName2").get(0).files;
            if (files.length > 0)
            {
                formData.append("File2", files[0]);  // Add the uploaded image content to the form data collection
            }
        }

        $.ajax({
            type: "post",
            url: '@Url.Action("Upload", "Api/FilesApi")',
            async: false,
            data: formData,
            contentType: false,
            processData: false,
            success: function (data, status)
            {
                alert(data);
            },
            error: function (xhr, status, err)
            {
                alert("ajax呼叫異常: " + status + "," + err);
            }
        });
   
    }
</script>
</head>
<body>
    <form id="Form1" enctype="multipart/form-data">
        <div align="center">
            <h2><label>檔案遠端上傳網站</label></h2>
            <table style="width: 1050px;" border="1">
                <tr style="height: 32px">
                    <td rowspan="2" style="width: 120px;text-align:center">
                        檔名
                    </td>
                    <td colspan="2" align="left">
                        <input ID="tbFileName1" name="tbFileName1" type="file" style="width: 96%;" multiple="multiple" />
                    </td>
                </tr>
                <tr style="height: 32px">
                    <td colspan="2" align="left">
                        <input ID="tbFileName2" name="tbFileName2" type="file" style="width: 96%;" multiple="multiple" />
                    </td>
                </tr>
                <tr style="height: 42px">
                    <td style="text-align: center">到資料夾</td>
                    <td style="width: 650px; text-align: left;">
                        <select ID="cbDirNames" style="width: 650px;">
                            <option>e:\temp\</option>
                            <option>e:\temp1\</option>
                        </select>
                    </td>
                    <td style="height: 42px; text-align:center;">
                        <input type="button" ID="bnUpload" value="上傳檔案" onclick="Upload()" style="width: 232px; height: 32px;" />
                    </td>                
                </tr>
            </table>
        </div>
    </form>
</body>
</html>
上述檢視檔案使用了jQuery獲取網頁三個元素的值,即上傳的資料夾名cbDirNames、上傳檔案tbFileName1和tbFileName2,該檢視使用jQuery的ajax同步提交多個檔案。需要說明如下幾點:
  1. @Url.Action用於產生 Url。該輔助方法可以根據路由表產生正確的相對地址,在ajax呼叫引數中也使用了該輔助方法。注意,上述程式碼的<script src=''>使用了不存在的動作jquery-1.12.4.min.js和控制器scripts,將產生一個類似MySite/scripts/jquery-1.12.4.min.js的Url。
  2. 資料物件FormData是一個名值表,提供了append函式增加名值對。如果是<input type='file' />元素,則自動新增檔案內容。該物件是2008年由html5引入,獲得目前主流的瀏覽器的支援。
  3. 在WebApi中,var httpRequest = HttpContext.Current.Request對應FormData,可以獲得名值對,也可以獲取檔名和內容。
  4. 檔案上傳網頁Index.cshtml的Form元素中必須有 enctype="multipart/form-data" 屬性。
上述程式在Windows7+IIS+.NET Framwwork 4.0下基於ASP.NET MVC4框架程式設計實現,在瀏覽器Firefox53+、Chrome57+、IE11和Windows Edge等測試通過。如下是實際執行的截圖。
後記:本文介紹的方法在開發機器上操作沒有任何問題,但釋出到客戶伺服器上就丟擲異常 Failed to execute, Failed to load XMLHttpRequest...。相關處理方法見拙文ASP.NET WebApi 上傳檔案時異常 Failed to execute send on XMLHttpRequest 的一個處理方法