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


前言

上一節我們講了簡單的上傳以及需要注意的地方,查相關資料時,感覺上傳里面涉及到的內容還是比較多,於是就將上傳這一塊分為幾節來處理,同時后續也會講到關於做上傳時遺漏的C#應該注意的地方,及時進行查漏補缺,盡量將這一塊完善起來。

引入

上一節我們講到了上傳這一塊,在評論中有園友提出未涉及到大文件的上傳這一塊,思前想后還是來試着做做,畢竟之前沒怎么去仔細考慮過這個問題,尤其還可以聯系實際開發中創建文件夾等一系列問題,同時關於上傳在網上隨便找找都充斥着大量的組件,我們何必再去造輪子,我需要做的是只是對基礎的回顧以及進一步深入以及在做的過程中發現一些細節性的問題並解決就足矣,不喜勿噴。

深入上傳

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

我們可以想象這一樣一個場景:比如在博客園中,每個博客者都可以上傳文件如圖片、腳本之類,我們可以通過園友名稱來創建每個園友上傳的文件,接下來我們來實現這樣的一個場景。

既然是對應博客的名稱創建文件,也就是需要對應的博客這樣的一個類。如下:

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

        public string Id { get; set; }
    }

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

(1)我們給出一個關於上傳的 UploadManager 靜態類,我們可以寫死上傳的文件夾名稱或者通過配置文件自定義上傳文件夾名稱。

        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);
        }

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

  <!--<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)循環讀取流到文件流中

         /// <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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM