分享一篇關於C#大文件上傳的整個過程

  • 2020 年 3 月 12 日
  • 筆記

簡單寫個小例子,記錄一下此次大文件上傳遇到的所有問題。

一、客戶端(使用winform窗體實現)

具體功能:

  • 點擊「選擇」按鈕,選擇要上傳的文件
  • 點擊「上傳文件」按鈕,上傳該文件調用UpLoad_Request(string address, string fileNamePath, string saveName, ProgressBar progressBar)方法
  • 在客戶端顯示上傳進度,已經時間,平均速度,上傳狀態,上傳大小

FileUpload 文件上傳類程式碼:

public class FileUpload      {          /// <summary>         /// 上傳文件         /// </summary>         /// <param name="address">文件上傳到伺服器的路徑</param>         /// <param name="fileNamePath">要上傳的本地路徑(全路徑)</param>         /// <param name="saveName">文件上傳後的名稱</param>         /// <returns>成功返回1,失敗返回0</returns>          public static int UpLoad_Request(string address, string fileNamePath, string saveName, ProgressBar progressBar)          {              int returnValue = 0;              //要上傳的文件              FileStream fs = new FileStream(fileNamePath, FileMode.Open, FileAccess.Read);              //二進位對象              BinaryReader r = new BinaryReader(fs);              //時間戳              string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");              byte[] boundaryBytes = Encoding.ASCII.GetBytes("rn--" + strBoundary + "rn");              //請求的頭部資訊              StringBuilder sb = new StringBuilder();              sb.Append("--");              sb.Append(strBoundary);              sb.Append("rn");              sb.Append("Content-Disposition: form-data; name="");              sb.Append("file");              sb.Append(""; filename="");              sb.Append(saveName);              sb.Append("";");              sb.Append("rn");              sb.Append("Content-Type: ");              sb.Append("application/octet-stream");              sb.Append("rn");              sb.Append("rn");              string strPostHeader = sb.ToString();              byte[] postHeaderBytes = Encoding.UTF8.GetBytes(strPostHeader);                // 根據uri創建HttpWebRequest對象              HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(address));              //對發送的數據不使用快取              httpReq.AllowWriteStreamBuffering = false;              //設置獲得響應的超時時間(3min)              httpReq.Timeout = 180000;              httpReq.KeepAlive = true;              httpReq.ProtocolVersion = HttpVersion.Version11;                httpReq.Method = "POST";              //對發送的數據不使用快取              //httpReq.AllowWriteStreamBuffering = true;              //設置獲得響應的超時時間(300s)              //httpReq.Timeout = 300000;              httpReq.ContentType = "multipart/form-data; boundary=" + strBoundary;              long length = fs.Length + postHeaderBytes.Length + boundaryBytes.Length;              long fileLength = fs.Length;              httpReq.ContentLength = length;              try              {                  progressBar.Maximum = int.MaxValue;                  progressBar.Minimum = 0;                  progressBar.Value = 0;                  //每次上傳8k                  int bufferLength = 8192;                  byte[] buffer = new byte[bufferLength]; //已上傳的位元組數                  long offset = 0;         //開始上傳時間                  DateTime startTime = DateTime.Now;                  int size = r.Read(buffer, 0, bufferLength);                  Stream postStream = httpReq.GetRequestStream();         //發送請求頭部消息                  postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);                  while (size > 0)                  {                      postStream.Write(buffer, 0, size);                      offset += size;                      progressBar.Value = (int)(offset * (int.MaxValue / length));                      TimeSpan span = DateTime.Now - startTime;                      double second = span.TotalSeconds;                      Application.DoEvents();                      size = r.Read(buffer, 0, bufferLength);                  }                  //添加尾部的時間戳                  postStream.Write(boundaryBytes, 0, boundaryBytes.Length);                  postStream.Close();                  //獲取伺服器端的響應                  WebResponse webRespon = httpReq.GetResponse();                  Stream s = webRespon.GetResponseStream();                  //讀取伺服器端返回的消息                  StreamReader sr = new StreamReader(s);                  String sReturnString = sr.ReadLine();                  s.Close();                  sr.Close();                  if (sReturnString == "Success")                  {                      progressBar.Value = 100;                      returnValue = 1;                  }                  else if (sReturnString == "Error")                  {                      returnValue = 0;                      progressBar.Value = 0;//錯誤就得重新上傳,進度條置零                  }              }              catch(Exception ex)              {                    Console.WriteLine(ex.Message);                  returnValue = 0;              }              finally              {                  fs.Close();                  r.Close();              }              return returnValue;          }        }

具體實現程式碼如下:

using System;  using System.Collections.Generic;  using System.ComponentModel;  using System.Data;  using System.Drawing;  using System.IO;  using System.Linq;  using System.Net;  using System.Text;  using System.Threading.Tasks;  using System.Windows.Forms;    namespace UpFileClient  {      public partial class Form1 : Form      {          public Form1()          {              InitializeComponent();          }          string filePath = "";          string FileName = "";          private void btnSelect_Click(object sender, EventArgs e)          {              //創建文件彈出選擇窗口(包括文件名)對象              OpenFileDialog ofd = new OpenFileDialog();              //判斷選擇的路徑              if (ofd.ShowDialog() == DialogResult.OK)              {                  this.txtSoundPath.Text = ofd.FileName.ToString();              }              filePath = this.txtSoundPath.Text;          }            private void btnUpLoad_Click(object sender, EventArgs e)          {              try {                  //上傳伺服器的地址(web服務)               string address = "http://localhost:61501/WebService/SaveFileWebForm.aspx";                  //上傳後文件保存的名稱                  string saveName = DateTime.Now.ToString("yyyyMMddHHmmss");                  int count = FileUpload.UpLoad_Request(address, filePath, saveName, this.progressBar1);                  if (count > 0)                  {                      MessageBox.Show("上傳文件成功!");                  }                  else                  {                      MessageBox.Show("上傳文件失敗!");                  }              }catch(Exception ex)              {                    Console.WriteLine(ex.Message);              }         }      }  }

介面顯示

二、伺服器端-提供文件上傳服務

1、創建一個web mvc項目,在創建一個webservice文件夾,在文件夾下創建一個SaveFileWebForm.axpx介面,

設置這個頁面為項目起始頁,打開這個頁面,右鍵查看程式碼,編寫伺服器端程式碼,運行項目

項目結構:

客戶端上傳文件時,伺服器端(SaveFileWebForm.axpx)需要訪問的到,(註:先運行伺服器程式項目,在運行客戶端程式項目)

文件保存路徑和文件保存名可以根據實際需要設置

伺服器端源碼:

using System;  using System.Collections.Generic;  using System.Linq;  using System.Web;  using System.Web.UI;  using System.Web.UI.WebControls;    namespace SaveFileWebService.WebService  {      public partial class SaveFileWebForm : System.Web.UI.Page      {          protected void Page_Load(object sender, EventArgs e)          {              if (Request.Files.Count > 0)              {                  try                  {                      //得到客戶端上傳的文件                      HttpPostedFile file = Request.Files[0];                      //伺服器端要保存的路徑                      string filePath = "D:\Test\" + file.FileName + ".mp3";                      file.SaveAs(filePath);                      //返回結果                      Response.Write("Success");                  }                  catch                  {                      Response.Write("Error");                  }              }              else              {                  Response.Write("Error1");              }          }      }  }

按照上面過程走下來,本來以為已經可以大功告成了,結果問題來了,小文件上傳沒問題,

超了4m的文件上傳不了,報錯引發的異常:「System.Net.WebException」(位於 System.dll 中)報400錯誤

在使用FileUpload控制項時不少人遇到過上傳文件失敗的問題,其實是出於安全的原因,.Net運行時對請求文件最大長度作了限制,開發者需要手動修改下配置文件。

這是什麼原因呢?我們仔細分析錯誤資訊,說是「超過了最大請求長度」。

原來是有最大長度限制!那應該在哪裡設置可以上傳更大的文件呢?

通過研究,可以修改web.config增大可上傳文件的大小限制。同時還可以設置最大執行時間。程式碼如下:

<httpRuntime maxRequestLength="204800" executionTimeout="600"/>

上述程式碼maxRequestLength的單位是KB,204800即是200MB。executionTimeout的單位是秒。

通過上述設置,FileUpload就可以上傳超過4m大文件了。

你可能還不明白httpRuntime的設置程式碼該插入到web.config哪個地方,如果放錯了,可能會導致web.config配置文件失效,從而影響網站的正常運行。請參考文章《httpRuntime程式碼放在web.config哪裡?深度了解httpRuntime》。

修改web.config後上傳大於30M的文件失敗

通過上面介紹的方法,可以上傳超過4m的大文件了,但是當上傳大於30m的文件時,卻又提示失敗了!

這時好像httpRuntime的maxRequestLength設置已經無效了。這又是什麼原因呢?

原來,IIS本身有請求長度限制!這時我們可以修改IIS配置來解決這個問題。

首先,打開IIS,如下圖

接下來,選擇自己的網站,停止,然後選中自己的站點,並雙擊「請求篩選」。

雙擊請求篩選之後,看到右邊操作中「編輯功能設置」,點擊「編輯功能設置」,打開介面

設置請求限制

在彈出的頁面中,我們看到默認請求限制正是30M,這時我們可以它改為更大的數字例如300M(300000000)以滿足自己的要求,根據自己的需求設置,我這裡設置102400000

請注意,修改了IIS的「請求篩選」後,web.config里同樣要設置httpRuntime的maxRequestLength的值大於30m。

到了這裡是不是以為已經完成了呢,開心ing(*^▽^*)

不不不,其實還有

最後一個坑

添加system.webServer節點 修改伺服器允許最大長度-requestLimits maxAllowedContentLength="1073741824"

<configuration>    <system.webServer>      <security>        <requestFiltering>          <!--修改伺服器允許最大長度-->          <requestLimits maxAllowedContentLength="1073741824"/>        </requestFiltering>      </security>    </system.webServer>  </configuration>

這個是我忘記設置的一個項,導致我浪費了一天時間,在糾結為何大文件上傳就是不行,該設置的已經設置了還是不行,都怪自己太粗心了,哎呀ε=(´ο`*)))

ok,到這裡是真的完成了,整個過程很坎坷,一波三折,希望看到我這篇文章的朋友們,以後少走彎路哈~

有疑問有建議的歡迎留言指導~

Tags: