狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

C#使用HttpWebRequest實現大文件上傳

admin
2017年3月22日 0:13 本文熱度 7186

Author:xuzhihong

Create Date:2011-06-03

Descriptions: WinForm程序使用HttpWebRequest實現大文件上傳

概述:

通常在WinForm程序中都是采用WebClient方式實現文件上傳功能,本身這個方式沒有問題,但是當需要上傳大文件比如說(300+M)的時候,那么WebClient將會報內存不足異常(Out of Memory Exceptions),究其原因是因為WebClient方式是一次性將整個文件全部讀取到本地內存中,然后再以數據流形式發送至服務器。本文將講述如何采用HttpWebRequest方式每次讀取固定大小數據片段(如4KB)發送至服務器,為大文件上傳提供解決方案,本文還將詳細講述將如何將“文件上傳”功能做為用戶自定義控件,實現模塊重用。

 

關鍵詞:HttpWebRequestWebClientOutOfMemoryExceptions

 

解決方案:

開始我在WinForm項目中實現文件上傳功能的時候,是采用WebClientWebClient myWebClient = new WebClient();)方式,這大部分情況都是正確的,但有時候會出現內存不足的異常(Out of Memory Exceptions),經常測試,發現是由于上傳大文件的時候才導致這問題。在網上查閱了一下其他網友的解決方案,最后找的發生異常的原因:“WebClient方式是一次性將整個文件全部讀取到本地內存中,然后再以數據流形式發送至服務器”,詳細請參考:http://blogs.msdn.com/b/johan/archive/2006/11/15/are-you-getting-outofmemoryexceptions-when-uploading-large-files.aspx 按照這個解釋,那么大文件上傳出現內存不足的異常也就不足為奇了。下面我將講述如何一步步使用HttpWebRequest方式來實現文件分塊上傳數據流至服務器。

按照慣例還是先預覽一下文件上傳最后的效果吧,如下圖所示:

使用HttpWebRequest實現大文件上傳 - 飛天心宏 - 飛天心宏的博客

 

界面分為兩部分,上面是文件基本信息,下面是文件上傳自定義控件,我這里實現的是一個案件上傳多個監控視頻功能。以下是詳細步驟:

第一步:創建用戶自定義控件BigFileUpload.xaml

文件上傳是一個非常常用的功能,為了所寫的程序能非常方便地多次重復使用,我決定將其處理為一個用戶自定義控件(UserControl)。

我們先在項目中創建一個FileUpload文件夾,在其目錄下新建一個WPF自定義控件文件命名為BigFileUpload.xaml,這樣就表示文件上傳是一個獨立的小模塊使用。之所以用WPF自定義控件是因為WPF頁面效果好看點,而且我想以后可能大部分C/S程序都會漸漸的由WinForm轉向WPF吧,當然創建Window Forms用戶控件也是沒有問題的。然后我們需要做一個下圖效果的頁面布局:

使用HttpWebRequest實現大文件上傳 - 飛天心宏 - 飛天心宏的博客

 

前臺設計代碼如下:

<UserControl x:Class="CHVM.FileUpload.BigFileUpload"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Height="160" Width="480">

    <Grid Height="160" Width="480" Background="White">

        <Label Height="28" HorizontalAlignment="Left"Margin="16,10,0,0" Name="label1" VerticalAlignment="Top" Width="53">文件</Label>

        <Label HorizontalAlignment="Left" Margin="15,52,0,80"Name="label2" Width="54">進度</Label>

        <ProgressBar Height="20" Margin="61,52,116,0"Name="progressBar1" VerticalAlignment="Top" />

        <TextBox Height="23" Margin="61,12,116,0"Name="txtBoxFileName" VerticalAlignment="Top"  />

        <Button Height="23" HorizontalAlignment="Right"Margin="0,10,35,0" Name="BtnBrowse" VerticalAlignment="Top" Width="75"Click="BtnBrowse_Click">瀏覽...</Button>

        <Button Height="23" HorizontalAlignment="Right"Margin="0,52,35,0" Name="BtnUpload" VerticalAlignment="Top" Width="75"Click="BtnUpload_Click">上傳</Button>

        <Label HorizontalAlignment="Left" Margin="16,0,0,44"Name="lblState" Width="183" Height="35" VerticalAlignment="Bottom">已上傳</Label>

        <Label Margin="231,0,35,44" Name="lblSize" Height="35"VerticalAlignment="Bottom">/</Label>

        <Label Height="28" HorizontalAlignment="Left"Margin="16,0,0,10" Name="lblTime" VerticalAlignment="Bottom"Width="183">已用時</Label>

        <Label Height="28" Margin="230,0,35,10" Name="lblSpeed"VerticalAlignment="Bottom">平均速度</Label>

    </Grid>

</UserControl>

 

 

后臺CS代碼:

public delegate void FilUploadHandler(EventFileUploadArg e);

    /// <summary>

    /// 自定義事件數據參數類

    /// </summary>

    public class EventFileUploadArg : EventArgs

    {

        private HttpWebRequestReturn hwr;

        /// <summary>

        /// 文件上傳服務器返回類

        /// </summary>

        public HttpWebRequestReturn HwrReturn

        {

            get

            {

                return hwr;

            }

            set

            {

                hwr = value;

            }

        }

        public EventFileUploadArg()

        {

            hwr = new HttpWebRequestReturn();

        }

        public EventFileUploadArg(HttpWebRequestReturn hwrReturn)

        {

            hwr = hwrReturn;

        }

    }

 

    /// <summary>

    /// BigFileUpload.xaml 的交互邏輯

    /// </summary>

    public partial class BigFileUpload : UserControl

    {

        public BigFileUpload()

        {

            InitializeComponent();

        }

 

        public event FilUploadHandler EventFileUpload;

 

        /// <summary>

        /// 服務器接收的地址 如:http://192.168.0.105:8078/Default.aspx

        /// </summary>

        public string ServerAddress

        {

            get;

            set;

        }

        /// <summary>

        /// 狀態標識是否上傳成功

        /// </summary>

        private bool IsSuccess

        {

            get;

            set;

        }

       

        /// <summary>

        /// 將本地文件上傳到指定的服務器(HttpWebRequest方法)

        /// </summary>

        /// <param name="address">文件上傳到的服務器</param>

        /// <param name="fileNamePath">要上傳的本地文件(全路徑)</param>

        /// <param name="saveName">文件上傳后的名稱</param>

        /// <param name="progressBar">上傳進度條</param>

        /// <returns>服務器反饋信息</returns>

        private HttpWebRequestReturn Upload_Request(stringaddressstring fileNamePathstring saveNameProgressBar progressBar)

        {

            HttpWebRequestReturn hwr;

 

            // 要上傳的文件

            FileStream fs = new FileStream(fileNamePath,FileMode.OpenFileAccess.Read);

            BinaryReader r = new BinaryReader(fs);

 

            //時間戳

            string strBoundary = "----------" +DateTime.Now.Ticks.ToString("x");

            byte[] boundaryBytes =Encoding.ASCII.GetBytes("\r\n--" + strBoundary + "\r\n");

 

            //請求頭部信息

            StringBuilder sb = new StringBuilder();

            sb.Append("--");

            sb.Append(strBoundary);

            sb.Append("\r\n");

            sb.Append("Content-Disposition: form-data; name=\"");

            sb.Append("file");

            sb.Append("\"; filename=\"");

            sb.Append(saveName);

            sb.Append("\"");

            sb.Append("\r\n");

            sb.Append("Content-Type: ");

            sb.Append("application/octet-stream");

            sb.Append("\r\n");

            sb.Append("\r\n");

 

            string strPostHeader = sb.ToString();

            byte[] postHeaderBytes =Encoding.UTF8.GetBytes(strPostHeader);

 

            // 根據uri創建HttpWebRequest對象

            HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(address));

            httpReq.Method = "POST";

 

            //對發送的數據不使用緩存【重要、關鍵】

            httpReq.AllowWriteStreamBuffering = false;

 

            //設置獲得響應的超時時間(300秒)

            httpReq.Timeout = 300000;

            httpReq.ContentType = "multipart/form-data; boundary=" + strBoundary;

            long length = fs.Length + postHeaderBytes.LengthboundaryBytes.Length;

            long fileLength = fs.Length;

            httpReq.ContentLength = length;

            try

            {

                progressBar.Maximum =fileLength;//int.MaxValue;

                progressBar.Minimum = 0;

                progressBar.Value = 0;

 

                //每次上傳4k

                int bufferLength = 4096;

                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 =offset;//(int)(offset * (int.MaxValue / length));

                    TimeSpan span = DateTime.Now -startTime;

                    double second =span.TotalSeconds;

                    lblTime.Content = "已用時:" +second.ToString("F2") + "";

                    if (second > 0.0001)

                    {

                        lblSpeed.Content = 平均速度:" + (offset / 1024 / second).ToString("0.00") + "KB/";

                    }

                    else

                    {

                        lblSpeed.Content = 平均速度太快,系統放棄計算";

                    }

                    //lblState.Content = "已上傳:" + (offset * 100.0 / length).ToString("F2") + "%";

                    lblState.Content = "已上傳:" + (offset * 100.0 / fileLength).ToString("F2") + "%";

                    //1024*1024=1048576

                    if (fileLength > 1048576) //根據文件是否大于1M,來使用單位【處理精度】

                    {

                        lblSize.Content = (offset/ 1048576.0).ToString("F2") + "M/" + (fileLength / 1048576.0).ToString("F2") + "M";

                    }

                    else

                    {

                        lblSize.Content = (offset/ 1024.0).ToString("F2") + "KB/" + (fileLength / 1024.0).ToString("F2") + "KB";

                    }

                   

                    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 serverMsg = sr.ReadLine();

                hwr =JSSerialize.Deserialize<HttpWebRequestReturn>(serverMsg);

                s.Close();

                sr.Close();

 

            }

            catch(Exception ex)

            {

                hwr = new HttpWebRequestReturn();

                hwr.success = false;

                hwr.errors = ex.Message;

            }

            finally

            {

                fs.Close();

                r.Close();

            }

 

            return hwr;

        }

 

        /// <summary>

        /// 瀏覽

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void BtnBrowse_Click(object sender,RoutedEventArgs e)

        {

            IsSuccess = false;

            System.Windows.Forms.OpenFileDialog ofd = newSystem.Windows.Forms.OpenFileDialog();

            ofd.Multiselect = false//單選

            ofd.Filter = "Video files (*.avi)|*.avi|All files (*.*)|*.*";

            ofd.FilterIndex = 2;

            ofd.RestoreDirectory = false;

            if (ofd.ShowDialog() ==System.Windows.Forms.DialogResult.OK)

            {

               txtBoxFileName.Text = ofd.FileName;

            }

        }

 

        /// <summary>

        /// 上傳

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void BtnUpload_Click(object sender,RoutedEventArgs e)

        {

            BtnUpload.IsEnabled =false;           

            string fileNamePath = txtBoxFileName.Text//本地欲上傳文件完整路徑

            if (fileNamePath == "")

            {

                MessageBox.Show("請選擇要上傳的文件路徑!","溫馨提示");

            }

            else if (!File.Exists(fileNamePath))

            {

                MessageBox.Show("選擇的文件不存在,可能已經被刪除,請重新選擇!""溫馨提示");

            }

            else

            {

                try

                {

                    string fileName =fileNamePath.Substring(fileNamePath.LastIndexOf("\\") + 1); //欲上傳文件名

                    string fileNameExt =fileName.Substring(fileName.LastIndexOf(".")); //文件后綴,包含"."

                    string saveName =fileName.Substring(0, fileName.Length - fileNameExt.Length) +DateTime.Now.ToString("yyMMddhhmmss") +DateTime.Now.Millisecond.ToString() + fileNameExt;

                    HttpWebRequestReturn hwr =Upload_Request(ServerAddressfileNamePathsaveName,progressBar1);                   

                    if (hwr.success//上傳成功

                    {

                        if (EventFileUpload !=null)

                        {

                           EventFileUploadArg arg = new EventFileUploadArg(hwr);

                           EventFileUpload(arg); //上傳后執行文件上傳的后續的自定義事件

                        }

                    }

                    else

                    {

                       MessageBox.Show(hwr.message);

                    }

                   

                }

                catch (System.Exception ex)

                {

                    MessageBox.Show(ex.Message);

                }

            }

            BtnUpload.IsEnabled = true;

        }

 

曾經在大學的時候,記得數字圖像處理老師給我們說過:“中國的書籍講的大部分都是理論,很少有真正將完整代碼寫出來的”。所以我每次寫文章的時候,都有個習慣就是盡可能完整的把代碼貼出來,一是怕自己文字功底太差表示不清楚,二是方便大家和自己以后理解。題外話少說,還是簡單的講述一下界面及代碼結構吧。

界面相當簡單,就是一個瀏覽按鈕和一個上傳按鈕,以及一些用于增加友好度的Label提示。瀏覽按鈕對應的事件BtnBrowse_Click,里面定義了一個OpenFileDialog用于選擇需要上傳的文件。上傳按鈕對應的事件BtnUpload_Click作了一些基本的驗證,然后調用了最關鍵的Upload_Request方法,同時執行了一個委托事件EventFileUpload(arg); //上傳后執行文件上傳的后續的自定義事件

Uplaod_Request方法帶有四個參數:

 /// <summary>

 /// 將本地文件上傳到指定的服務器(HttpWebRequest方法)

 /// </summary>

 /// <param name="address">文件上傳到的服務器(服務器接收的地址如:http://192.168.0.105:8078/Default.aspx )</param>

 /// <param name="fileNamePath">要上傳的本地文件(全路徑)</param>

 /// <param name="saveName">文件上傳后的名稱</param>

 /// <param name="progressBar">上傳進度條</param>

/// <returns>服務器反饋信息</returns>

private HttpWebRequestReturn Upload_Request(string addressstringfileNamePathstring saveNameProgressBar progressBar){}

值得一提的是這里的返回類型HttpWebRequestReturn(點擊查看定義)是為了和數據庫對應自己定義的一個類,繼承自統一返回類型TwiReturn(點擊查看定義),里面記錄了文件服務器反饋的綜合信息。

 

第二步:創建服務器響應程序BigFileUploadServerApp

很顯然文件上傳至服務器后需要有個對應的響應程序。那么我們再創建一個單獨的Web應用程序(命名為:BigFileUploadServerApp),發布在服務器中的IIS上,只需要一個默認的Default.aspx頁面和一個FileUpload空文件夾即可,我們將FileUpload文件夾所存放的目錄作為文件上傳至服務器存放的目錄。

Default.aspx.cs代碼也相當簡單:

        protected void Page_Load(object senderEventArgs e)

        {

            HttpWebRequestReturn hwr = newHttpWebRequestReturn();

            hwr.hasRight = true;

            if (Request.Files.Count > 0)

            {               

                try

                {

                    HttpPostedFile file =Request.Files[0];

                    string filePath =this.MapPath("FileUpload") + "\\" + file.FileName;

                    file.SaveAs(filePath);

                    hwr.FileName = file.FileName;

                    hwr.FileFullName = filePath;

                    hwr.ContentLength =file.ContentLength;

                    IPHostEntry hostInfo  =Dns.GetHostEntry(Server.MachineName);

                    hwr.ServerIP =hostInfo.AddressList[0].ToString();

                    hwr.success = true;

                }

                catch (Exception ex)

                {

                    hwr.errors = ex.Message;

                }

            }

            else

            {

                hwr.errors = "服務器沒接收到上傳的文件信息,請檢查上傳的文件是否為空文件!";

            }

            string strReturn = JSSerialize.Serialize(hwr);

            Response.Write(strReturn);

            Response.End();

        }

返回類型記錄了另存為的文件名FileName,文件在服務器中的全路徑FileFullName,服務器IP地址ServerIP等信息,JSSerialize.Serialize()(點擊查看定義)方法是將對象序列化為字符串。最后需要說明的是:微軟為了防止拒絕服務攻擊,對文件上傳做了一個大小限制,最大默認為4M,然后我們使用HttpWebRequest方法將會受到其影響。為了突破這個限制,那么我們需要在Web.Config文件中的system.web節點下增加一個httpRuntime配置,

  <system.web>

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

  </system.web>

其中MaxRequestLength單位為KB,executionTimeout單位為秒,大小自己根據實際情況進行控制。

文件上傳至服務器之后,我們還需要將文件基本信息記錄到對應的數據庫中,那么在執行“上傳”事件時我們還需要執行自定義后續操作。由于我們做的是一個通用的文件上傳功能,所以不能直接將業務邏輯寫在BtnUpload_Click方法中,因為每個地方上傳處理的邏輯也許并不一樣。這個時候當然就該是偉大的委托上場了,在此我們定義了一個FileUploadHandler委托,定義如下:

public delegate void FilUploadHandler(EventFileUploadArge);

其參數有點特別,不是常規的EventArgs,而是自定義繼承自EventArgsEventFileUploadArg,定義如下:

    /// <summary>

    /// 自定義事件數據參數類

    /// </summary>

    public class EventFileUploadArg : EventArgs

    {

        private HttpWebRequestReturn hwr;

        /// <summary>

        /// 文件上傳服務器返回類

        /// </summary>

        public HttpWebRequestReturn HwrReturn

        {

            get

            {

                return hwr;

            }

            set

            {

                hwr = value;

            }

        }

        public EventFileUploadArg()

        {

            hwr = new HttpWebRequestReturn();

        }

        public EventFileUploadArg(HttpWebRequestReturn hwrReturn)

        {

            hwr = hwrReturn;

        }

    }

為什么要定義這么一個參數呢?因為我們在服務器接收文件后得到了一些反饋信息(是一個HttpWebRequestReturn類的實例),那么在處理后續的邏輯的時候,是希望了解這些信息的,所謂的了解其實就是能夠訪問反饋信息,那么無疑于這種方式公開出來是非常合理的。

 

第三步:應用

到這里我們已經把自定義用戶控件做好了,但是還沒真正使用。這么這一步我們將討論如何使用它。為了實現前面演示的效果我們新建一個WinForm窗體頁面暫且命名為(FormVideoFileUpload.cs),然后做一個簡單的布局,如下圖:

使用HttpWebRequest實現大文件上傳 - 飛天心宏 - 飛天心宏的博客

 

上面的都是文件基本信息,下面的是一個Panel用于承載我們前面做好的“自定義文件上傳控件BigFileUpload.xaml”,后臺cs代碼如下:

public partial class FormVideoFileUpload : Form

    {

        public FormVideoFileUpload()

        {

            InitializeComponent();

 

            AddBfuControl();

        }

 

        /// <summary>

        /// 增加文件上傳自定義控件

        /// </summary>

        public void AddBfuControl()

        {

            BigFileUpload bfu = new BigFileUpload();

            bfu.EventFileUpload += newFilUploadHandler(Bfu_BtnUpload_Click);

            bfu.ServerAddress = CommPar.VM_VideoFilesUrl;

            ElementHost elHost = new ElementHost();

            elHost.Dock = DockStyle.None;

            elHost.Width = panel1.Width;

            elHost.Height = panel1.Height;

            elHost.Child = bfu;

           panel1.Controls.Add(elHost);           

        }

 

        /// <summary>

        /// 文件上傳完成的自定義事件

        /// </summary>

        /// <param name="arg"></param>

        public void Bfu_BtnUpload_Click(EventFileUploadArg arg)

        {

            if (arg.HwrReturn.success)

            {

                TMEDIAS medias = new TMEDIAS();

                medias.MEDIASEED = txtMediaSeed.Text;

                medias.MEDIASOURCE = txtMediaSource.Text;

                medias.CASENUMBER = txtCaseNumber.Text;

                medias.REMARK = txtRemark.Text;

                medias.FILENAME = arg.HwrReturn.FileName;

                medias.FILEFULLNAME =arg.HwrReturn.FileFullName;

                medias.SERVERIP = arg.HwrReturn.ServerIP;

                medias.UPDATETIME =DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

                medias.OPERATORID = 6;

                medias.OPERATOR = "趙精偉";

                TwiReturn twi =UsingBLL.medias.Add(medias);

                if (twi.success)

                {

                    DialogResult dResult =MessageBox.Show("恭喜你文件上傳成功,是否繼續上傳視頻文件?""恭喜",MessageBoxButtons.YesNoMessageBoxIcon.Question);

                    if (dResult == DialogResult.Yes)

                    {

                        panel1.Controls.Clear();

                        AddBfuControl();

                    }

                    else

                    {

                        this.Hide();

                    }

                }

                else

                {

                    MessageBox.Show(twi.message"提示");

                }             

               

            }

            else

            {

                MessageBox.Show(arg.HwrReturn.message,"提示");

            }

        }

}

 

 

經過不懈的努力,和這么長時間的耐心,到這里已經完成了我們所要做的工作了,看看我們的功能界面吧,不容易呀!

使用HttpWebRequest實現大文件上傳 - 飛天心宏 - 飛天心宏的博客

 

這里有個提示框提示用戶是否繼續上傳,如果是那么程序將刷新一下用戶控件,但是上面的文件案件基本信息仍然保留,這樣就做到了我所希望的一個案件對應上傳多個視頻的效果。

 

 

說在最后

該解決方案成功實現了基于HttpWebRequest的方式實現大文件上傳,相對來說這個界面還是挺好看的。對應大文件上傳有人也許會說用FTP的方式處理,聽人說配置有點復雜,由于我個人比較懶,所以沒去親自試驗,以后有機會再試試FTP的方式,只有我親自試成功了,我才會寫出來。

最后,由于個人技術水平和寫作能力的限制,文章有不足之處再所難免,還望大家批評指正,如果發現問題,我也將會盡快修改。知錯、認錯、改錯。


該文章在 2017/3/22 0:13:01 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved