ASP.NET MVC之文件上傳【二】(九)

前言

上一節咱們講了簡單的上傳以及須要注意的地方,查相關資料時,感受上傳裏面涉及到的內容仍是比較多,因而就將上傳這一塊分爲幾節來處理,同時後續也會講到關於作上傳時遺漏的C#應該注意的地方,及時進行查漏補缺,儘可能將這一塊完善起來。web

引入

上一節咱們講到了上傳這一塊,在評論中有園友提出未涉及到大文件的上傳這一塊,思前想後仍是來試着作作,畢竟以前沒怎麼去仔細考慮過這個問題,尤爲還能夠聯繫實際開發中建立文件夾等一系列問題,同時關於上傳在網上隨便找找都充斥着大量的組件,咱們何須再去造輪子,我須要作的是隻是對基礎的回顧以及進一步深刻以及在作的過程當中發現一些細節性的問題並解決就足矣,不喜勿噴。測試

深刻上傳

再次聲明對於上傳能夠顯示上傳進度之類咱們不去作過多探討,有這樣的組件,自行去找,咱們只需實現比較核心的這一塊便可。ui

咱們能夠想象這同樣一個場景:好比在博客園中,每一個博客者均可以上傳文件如圖片、腳本之類,咱們能夠經過園友名稱來建立每一個園友上傳的文件,接下來咱們來實現這樣的一個場景。spa

既然是對應博客的名稱建立文件,也就是須要對應的博客這樣的一個類。以下:
3d

    public class BlogSample
    {
        public string UserName { get; set; }

        public string Id { get; set; }
    }

咱們經過博客名稱來建立文件夾並在該文件夾下以惟一的Id來建立子文件夾,在該Id文件夾下的附件( atttachment )中存儲上傳的文件。接下來咱們須要梳理整個上傳文件的過程。難道就把要上傳的文件直接到上傳到對應的文件夾嗎,這麼作顯然不是最優的,當有上傳中斷時則在文件夾建立的文件不是完整的則是垃圾文件,而咱們直接先建立一個臨時文件,即便上傳失敗咱們能夠按期清理臨時文件也就是垃圾文件,若未中斷,上傳完畢時則將臨時文件移動到咱們對應的文件夾中。經過咱們實際下載文件時很明顯看的出也是這麼作的。接下來咱們開始進行實現。code

(1)咱們給出一個關於上傳的 UploadManager 靜態類,咱們能夠寫死上傳的文件夾名稱或者經過配置文件自定義上傳文件夾名稱。orm

        static UploadManager()
        {
            //從配置文件中獲取上傳文件夾
            if (String.IsNullOrWhiteSpace(WebConfigurationManager.AppSettings["UploadFolder"]))
                UploadFolderRelativePath = @"~/upload";
            else
                UploadFolderRelativePath = WebConfigurationManager.AppSettings["UploadFolder"];

            UploadFolderPhysicalPath = HostingEnvironment.MapPath(UploadFolderRelativePath);

            if (!Directory.Exists(UploadFolderPhysicalPath))
                Directory.CreateDirectory(UploadFolderPhysicalPath);
        }

上述已經代表能夠自定義上傳文件夾在配置文件中(給出上傳虛擬路徑),例如以下:blog

  <!--<add key="UploadFolder" value="~/UploadFile/">-->

(2)保存文件的核心方法圖片

        [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
        public static bool SaveFile(Stream stream, string fileName, string userName, string guid)
        {
            string tempPath = string.Empty, targetPath = string.Empty;

            try
            {
                string tempFileName = GetTempFilePath(fileName);

                if (userName != null)
                {
                    var contentType = userName;
                    var contentId = guid;

                    tempPath = GetTempFilePath(tempFileName);
                    targetPath = GetTargetFilePath(fileName, contentType, contentId, string.Empty, FilesSubdir);


                    //若上傳文件夾中子文件夾未存在則建立
                    var file = new FileInfo(targetPath);
                    if (file.Directory != null && !file.Directory.Exists)
                        file.Directory.Create();

                    using (FileStream fs = File.Open(tempPath, FileMode.Append))
                    {
                        if (stream.Length > 0)
                        {
                            SaveFile(stream, fs);
                        }
                        fs.Close();
                    }
                    //上傳完畢將臨時文件移動到目標文件
                    File.Move(tempPath, targetPath);
                }
            }
            catch (Exception)
            {
                // 若上傳出錯,則刪除上傳到文件夾文件
                if (File.Exists(targetPath))
                    File.Delete(targetPath);

                // 刪除臨時文件
                if (File.Exists(tempPath))
                    File.Delete(tempPath);

                return false;
            }
            finally
            {
                // 刪除臨時文件
                if (File.Exists(tempPath))
                    File.Delete(tempPath);
            }
            return true;
        }

(3)循環讀取流到文件流中ip

         /// <summary>
        /// 循環讀取流到文件流中
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="fs"></param>
        public static void SaveFile(Stream stream, FileStream fs)
        {
            var buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                fs.Write(buffer, 0, bytesRead);
            }
        }

(4)開始寫入測試數據,進行調用方法:

            var testSample = new BlogSample() { UserName = "xpy0928", Id = Guid.NewGuid().ToString("N") };
            if (ModelState.IsValid)
            {
                var fileName = bModel.BlogPhoto.FileName;
                var success = UploadManager.SaveFile(bModel.BlogPhoto.InputStream, fileName, testSample.UserName, testSample.Id);
                if (!success)
                {
                    // TODO(your code)
                }
                //var filePath = Server.MapPath(string.Format("~/{0}", "File"));
                //bModel.BlogPhoto.SaveAs(Path.Combine(filePath, fileName));
                ModelState.Clear();
            }

接下來咱們來進行測試,經過上傳一個84M的文件來看看效果(稍等片刻,文件有點大)。

很差意思,令我大失所望,和昨天出現的錯誤不同,今天出錯是:超過最大請求長度。咱們接下來再來看看昨天所說,個人IIS爲10.0,也就是在IIS 7+上,經過昨天那樣設置應該是沒問題的,難道和另一個設置有關嗎,咱們看看配置文件中的配置。

<httpRuntime targetFramework="4.5"/>

未進行設置,超過其默認設置28.6M就出錯了嗎,咱們再設置爲2G看看。

<httpRuntime targetFramework="4.5" executionTimeout="1100"  maxRequestLength="2147483647"/>

好,上傳成功也未出現上述錯誤。

結語

這一節咱們講了一下利用流來進行大文件的處理,不過仍是出現了一點小問題,和昨天再一塊兒作一次總結:

(1)在IIS 5和IIS 6中,默認文件上傳的最大爲4兆,當上傳的文件大小超過4兆時,則會獲得錯誤信息,可是咱們經過以下來設置文件大小。

<system.web>
  <httpRuntime maxRequestLength="2147483647" executionTimeout="100000" />
</system.web>

(2)在IIS 7+,默認文件上傳的最大爲28.6兆,當超過其默認設置大小,一樣會獲得錯誤信息,可是咱們卻能夠經過以下來設置文件上傳大小(同時也要進行如上設置)

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="2147483647" />
    </requestFiltering>
  </security>
</system.webServer>

關於在配置文件中如何設置文件大小而不出錯,總算作了一個最終的總結,有收穫,繼續Fighting。

相關文章
相關標籤/搜索