2012-07-14 376 views
1

要確保我做的一切正確的,在幫助別人了,我決定創建一個測試應用程序上傳文件的希望和張貼在這裏在我的問題上尋求幫助。希望它可以幫助別人,同時幫助我! ;)使用本地.NET庫FTP進度條不顯示正確的進度

值得注意: - 我測試這種通過速度較慢的連接,並限制了FTP用戶帳戶的速度約爲10KB/s的只是測試了這一點。

問題: 當使用我的新創建的線程(功能UploadAFile-> UF_Thread)上傳文件,我在剪斷,它的下方,發送一個數據包,即使requestStream.Write()返回時,允許線程繼續我的服務器顯示數據包仍在發送。在發送100個數據包3秒鐘的時間內,在我的循環中發送所有數據包,同時在服務器上傳輸文件需要大約26秒(在這段時間,我的應用程序剛剛坐下並等待它完成,顯示沒有進度完全是因爲23秒前它根據GUI完成)。

預期結果: 我需要找到一種方法來設置bStillSending爲真,直到數據包完成。我想等待每個數據包。

開始剪斷,它

DateTime startTimestamp = DateTime.Now; 
requestStream.Write(buffer, 0, bytesRead); 

//Wait for packet to get to server 
bool bStillSending = false; 
do 
{ 
    Thread.Sleep(50); 
} while (bStillSending); 

bytesRead = sourceStream.Read(buffer, 0, ufParams.PacketSize); 
lBytesSent += bytesRead; 

TimeSpan tsUploadTime = DateTime.Now.Subtract(startTimestamp); 
if (tsUploadTime.Milliseconds > 0) lstSpeedInBytesPerMillisecond.Add(bytesRead/tsUploadTime.Milliseconds); 
else lstSpeedInBytesPerMillisecond.Add(bytesRead); 
sSpeed = ConvertBytesToLargestType(lstSpeedInBytesPerMillisecond.Last()); 
Raise_UploadFile_Progress_Event(bytesRead, lBytesSent, sourceStream.Length, sSpeed); 

年底剪斷,它

啓動整個應用程序的

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.IO; 
using System.Net; 
using System.Threading; 
using System.Windows; 

namespace WindowsFormsApplication1 
{ 
    class UploadFile 
    { 
     #region Private class variables 
     private ManualResetEvent m_MRE_StopUpload = new ManualResetEvent(false); 
     #endregion 

     #region Private class structures 
     private class UploadFile_PARAMS 
     { 
      #region Local private class variables 
      private int m_iThreadID = 0; 
      private ManualResetEvent m_MRE_StopUpload = new ManualResetEvent(false); 
      private string m_sServer = ""; 
      private string m_sUser = ""; 
      private string m_sPass = ""; 
      private string m_sSourcePath = ""; 
      private string m_sDestPath = ""; 
      private int m_iPacketSize = 512; 
      #endregion 

      #region Properties 
      public int ThreadID 
      { 
       get 
       { 
        return m_iThreadID; 
       } 
       set 
       { 
        m_iThreadID = value; 
       } 
      } 

      public ManualResetEvent MRE_StopUpload 
      { 
       get 
       { 
        return m_MRE_StopUpload; 
       } 
       set 
       { 
        m_MRE_StopUpload = value; 
       } 
      } 

      public string Server 
      { 
       get 
       { 
        return m_sServer; 
       } 
       set 
       { 
        m_sServer = value; 
       } 
      } 

      public string User 
      { 
       get 
       { 
        return m_sUser; 
       } 
       set 
       { 
        m_sUser = value; 
       } 
      } 

      public string Pass 
      { 
       get 
       { 
        return m_sPass; 
       } 
       set 
       { 
        m_sPass = value; 
       } 
      } 

      public string SourcePath 
      { 
       get 
       { 
        return m_sSourcePath; 
       } 
       set 
       { 
        m_sSourcePath = value; 
       } 
      } 

      public string DestPath 
      { 
       get 
       { 
        return m_sDestPath; 
       } 
       set 
       { 
        m_sDestPath = value; 
       } 
      } 

      public int PacketSize 
      { 
       get 
       { 
        return m_iPacketSize; 
       } 
       set 
       { 
        m_iPacketSize = value; 
       } 
      } 
      #endregion 
     } 
     #endregion 

     #region Public class methods 
     //Public method UploadFile 
     public void UploadSingleFile(string sServer, string sUser, string sPass, string sSourcePath, string sDestPath, int iPacketSize) 
     { 
      UploadFile_PARAMS ufParams = new UploadFile_PARAMS(); 
      ufParams.Server = sServer; 
      ufParams.User = sUser; 
      ufParams.Pass = sPass; 
      ufParams.SourcePath = sSourcePath; 
      ufParams.DestPath = sDestPath; 
      ufParams.PacketSize = iPacketSize; 
      ufParams.MRE_StopUpload = m_MRE_StopUpload; 

      UploadAFile(ufParams); 
     } 

     //Public method CancelUpload 
     public void CancelUpload() 
     { 
      m_MRE_StopUpload.Set(); 
     } 
     #endregion 

     #region Public class events 
     //Public event UploadFile_Progress 
     public delegate void UploadFile_Progress_EventHandler(long lBytesSent, long lTotalBytesSent, long lTotalFileSizeBytes, string sSpeed); 
     public event UploadFile_Progress_EventHandler UploadFile_Progress; 
     private void Raise_UploadFile_Progress_Event(long lBytesSent, long lTotalBytesSent, long lTotalFileSizeBytes, string sSpeed) 
     { 
      if (this.UploadFile_Progress != null) 
      { 
       //The event handler exists so raise the event to it 
       this.UploadFile_Progress(lBytesSent, lTotalBytesSent, lTotalFileSizeBytes, sSpeed); 
      } 
     } 

     //Public event UploadFile_Complete 
     public delegate void UploadFile_Complete_EventHandler(long lTotalBytesSent, long lTotalFileSizeBytes, bool bError, string sAverageSpeed, TimeSpan tsTimeItTookToUpload); 
     public event UploadFile_Complete_EventHandler UploadFile_Complete; 
     private void Raise_UploadFile_Complete_Event(long lTotalBytesSent, long lTotalFileSizeBytes, bool bError, string sAverageSpeed, TimeSpan tsTimeItTookToUpload) 
     { 
      if (this.UploadFile_Complete != null) 
      { 
       //The event handler exists so raise the event to it 
       this.UploadFile_Complete(lTotalBytesSent, lTotalFileSizeBytes, bError, sAverageSpeed, tsTimeItTookToUpload); 
      } 
     } 

     //Public event UploadFile_Error 
     public delegate void UploadFile_Error_EventHandler(string sErrorMessage); 
     public event UploadFile_Error_EventHandler UploadFile_Error; 
     private void Raise_UploadFile_Error_Event(string sErrorMessage) 
     { 
      if (this.UploadFile_Error != null) 
      { 
       //The event handler exists so raise the event to it 
       this.UploadFile_Error(sErrorMessage); 
      } 
     } 
     #endregion 

     #region Private class functions 
     //Private class function ConvertBytesToLargestType 
     private string ConvertBytesToLargestType(long lBytesAMillisecond) 
     { 
      //Convert the byte count to the highest type 
      double dKiloBytesAMillisecond = (double)lBytesAMillisecond/(double)1024; 
      double dMegaBytesAMillisecond = dKiloBytesAMillisecond/(double)1024; 
      double dGigaBytesAMillisecond = dMegaBytesAMillisecond/(double)1024; 

      if (dGigaBytesAMillisecond >= 1) 
      { 
       return dGigaBytesAMillisecond.ToString("#.##") + "Gb/s"; 
      } 

      if (dMegaBytesAMillisecond >= 1) 
      { 
       return dMegaBytesAMillisecond.ToString("#.##") + "Mb/s"; 
      } 

      if (dKiloBytesAMillisecond >= 1) 
      { 
       return dKiloBytesAMillisecond.ToString("#") + "kb/s"; 
      } 

      return lBytesAMillisecond.ToString("#") + "b/s"; 
     } 
     #endregion 

     #region Private class multi-threading functions 
     //Thread creation for UploadAFile 
     System.Threading.Thread threadUF; 
     private void UploadAFile(UploadFile_PARAMS ufParams) 
     { 
      //Make sure to destory any old objects 
      if (threadUF != null) 
      { 
       if (threadUF.IsAlive) threadUF.Abort(); 
       while (threadUF.IsAlive) Application.DoEvents(); 
       threadUF = null; 
       GC.Collect(); 
      } 

      //Make sure the params object is not null 
      if (ufParams == null) return; 

      //Create the new thread 
      threadUF = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UF_Thread)); 
      threadUF.IsBackground = true; 
      ufParams.ThreadID = threadUF.ManagedThreadId; 

      //Start the thread 
      threadUF.Start(ufParams); 
     } 
     private void UF_Thread(Object oParameter) 
     { 
      //Extract (cast) the params 
      UploadFile_PARAMS ufParams = (UploadFile_PARAMS)oParameter; 

      FtpWebRequest request; 
      FtpWebResponse response; 

      Stream sourceStream = new MemoryStream(); 
      Stream requestStream = sourceStream; 

      long lBytesSent = 0; 
      string sSpeed = ""; 
      TimeSpan tsTimeToUploadFile; 
      DateTime dtStartedUpload = DateTime.Now; 
      List<long> lstSpeedInBytesPerMillisecond = new List<long>(); 

      try 
      { 
       request = (FtpWebRequest)WebRequest.Create("ftp://" + ufParams.Server + ufParams.DestPath); 
       request.Method = WebRequestMethods.Ftp.UploadFile; 
       request.Credentials = new NetworkCredential(ufParams.User, ufParams.Pass); 

       sourceStream = new FileStream(ufParams.SourcePath, FileMode.Open); 

       requestStream = request.GetRequestStream(); 
       request.ContentLength = sourceStream.Length; 

       byte[] buffer = new byte[ufParams.PacketSize]; 
       int bytesRead = sourceStream.Read(buffer, 0, ufParams.PacketSize); 

       do 
       { 
        DateTime startTimestamp = DateTime.Now; 
        requestStream.Write(buffer, 0, bytesRead); 

        //Wait for packet to get to server 
        bool bStillSending = false; 
        do 
        { 
         Thread.Sleep(50); 
        } while (bStillSending); 

        bytesRead = sourceStream.Read(buffer, 0, ufParams.PacketSize); 
        lBytesSent += bytesRead; 

        TimeSpan tsUploadTime = DateTime.Now.Subtract(startTimestamp); 
        if (tsUploadTime.Milliseconds > 0) lstSpeedInBytesPerMillisecond.Add(bytesRead/tsUploadTime.Milliseconds); 
        else lstSpeedInBytesPerMillisecond.Add(bytesRead); 
        sSpeed = ConvertBytesToLargestType(lstSpeedInBytesPerMillisecond.Last()); 
        Raise_UploadFile_Progress_Event(bytesRead, lBytesSent, sourceStream.Length, sSpeed); 
       } while (bytesRead > 0 && ufParams.MRE_StopUpload.WaitOne(0) == false); 

       if (ufParams.MRE_StopUpload.WaitOne(0)) throw new System.Net.WebException("Upload was canceled!"); 

       //Close the stream to show we are finished with the file 
       requestStream.Close(); 

       response = (FtpWebResponse)request.GetResponse(); 
       if (lstSpeedInBytesPerMillisecond.Count > 0) 
       { 
        sSpeed = ConvertBytesToLargestType(Convert.ToInt64(lstSpeedInBytesPerMillisecond.Average())); 
       } 
       else sSpeed = "0 b/s"; 
       tsTimeToUploadFile = DateTime.Now.Subtract(dtStartedUpload); 
       Raise_UploadFile_Complete_Event(lBytesSent, sourceStream.Length, false, sSpeed, tsTimeToUploadFile); 
      } 
      catch (Exception ex) 
      { 
       Raise_UploadFile_Error_Event(ex.GetType().Name + ": " + ex.Message); 
       if (lstSpeedInBytesPerMillisecond.Count > 0) 
       { 
        sSpeed = ConvertBytesToLargestType(Convert.ToInt64(lstSpeedInBytesPerMillisecond.Average())); 
       } 
       else sSpeed = "0 b/s"; 
       tsTimeToUploadFile = DateTime.Now.Subtract(dtStartedUpload); 
       Raise_UploadFile_Complete_Event(lBytesSent, sourceStream.Length, true, sSpeed, tsTimeToUploadFile); 
      } 
      finally 
      { 
       request = null; 

       sourceStream.Close(); 
       sourceStream = null; 

       requestStream.Close(); 
       requestStream = null; 
      } 
     } 
     #endregion 
    } 

    public partial class Form1 : Form 
    { 
     //Define the upload object as a global form object 
     UploadFile upload; 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     #region Buttons 
     private void button1_Click(object sender, EventArgs e) 
     { 
      OpenFileDialog openFileDialog1 = new OpenFileDialog(); 

      openFileDialog1.InitialDirectory = "c:\\"; 
      openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"; 
      openFileDialog1.FilterIndex = 2; 
      openFileDialog1.RestoreDirectory = true; 

      if (openFileDialog1.ShowDialog() == DialogResult.OK) 
      { 
       if (File.Exists(openFileDialog1.FileName)) 
       { 
        label1.Text = openFileDialog1.FileName; 
        listView1.Items.Add("Selected file to upload."); 
       } 
       else 
       { 
        label1.Text = ""; 
        listView1.Items.Add("File choosen does not exist!"); 
       } 
      } 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      if (!File.Exists(label1.Text)) 
      { 
       listView1.Items.Add(DateTime.Now.ToString("hh:mm:ss:f") + " Error! File selected does not exist!"); 
       return; 
      } 

      listView1.Items.Add(DateTime.Now.ToString("hh:mm:ss:f") + " Starting the upload process..."); 

      progressBar1.Minimum = 0; 
      progressBar1.Maximum = 100; 

      upload = new UploadFile(); 
      upload.UploadFile_Progress += new UploadFile.UploadFile_Progress_EventHandler(upload_UploadFile_Progress); 
      upload.UploadFile_Complete += new UploadFile.UploadFile_Complete_EventHandler(upload_UploadFile_Complete); 
      upload.UploadFile_Error += new UploadFile.UploadFile_Error_EventHandler(upload_UploadFile_Error); 
      upload.UploadSingleFile("ftp.xentar.com", "arvo", "S1mP13", label1.Text, "/arvo/" + Path.GetFileName(label1.Text), 1024); 
     } 

     private void button3_Click(object sender, EventArgs e) 
     { 
      listView1.Items.Add(DateTime.Now.ToString("hh:mm:ss:f") + " Canceling upload..."); 
      upload.CancelUpload(); 
     } 
     #endregion 

     #region UploadFile object events 
     private void upload_UploadFile_Error(string sErrorMessage) 
     { 
      listView1.Items.Add(DateTime.Now.ToString("hh:mm:ss:f") + " Error! " + sErrorMessage); 
     } 

     private void upload_UploadFile_Complete(long lTotalBytesSent, long lTotalFileSizeBytes, bool bError, string sAverageSpeed, TimeSpan tsTimeItTookToUpload) 
     { 
      if (bError) 
      { 
       AddItemToLog("Error uploading file!"); 
      } 
      else 
      { 
       AddItemToLog("Upload complete!"); 
       AddItemToLog("It took " + tsTimeItTookToUpload.ToString("mm") + " minutes and " + tsTimeItTookToUpload.ToString("ss") + " seconds to upload the file."); 
       UpdateProgressBar(100, lTotalBytesSent); 
      } 
     } 

     private void upload_UploadFile_Progress(long lBytesSent, long lTotalBytesSent, long lTotalFileSizeBytes, string sSpeed) 
     { 
      int iProgressComplete = Convert.ToInt32(((double)lTotalBytesSent/(double)lTotalFileSizeBytes) * 100); 
      UpdateProgressBar(iProgressComplete, lTotalBytesSent); 
     } 
     #endregion 

     #region Thread-safe functions 
     private void AddItemToLog(string sLogEntry) 
     { 
      //Send to tread safe call 
      ThreadSafe_AddItemToLog(sLogEntry); 
     } 
     delegate void ThreadSafe_AddItemToLog_Callback(string sLogEntry); 
     private void ThreadSafe_AddItemToLog(string sLogEntry) 
     { 
      if (this.InvokeRequired) 
      { 
       ThreadSafe_AddItemToLog_Callback d = 
        new ThreadSafe_AddItemToLog_Callback(ThreadSafe_AddItemToLog); 
       try 
       { 
        this.Invoke(d, new object[] { sLogEntry }); 
       } 
       catch 
       { 
        //ObjectDisposedException 
       } 
      } 
      else 
      { 
       //Send the call on to the normal function 
       ThreadSafe_AddItemToLog_Proc(sLogEntry); 
      } 
     } 
     private void ThreadSafe_AddItemToLog_Proc(string sLogEntry) 
     { 
      listView1.Items.Add(DateTime.Now.ToString("hh:mm:ss:f") + " " + sLogEntry); 
     } 

     private void UpdateProgressBar(int iProgressBarValue, long lTotalBytesSent) 
     { 
      //Send to tread safe call 
      ThreadSafe_UpdateProgressBar(iProgressBarValue, lTotalBytesSent); 
     } 
     delegate void ThreadSafe_UpdateProgressBar_Callback(int iProgressBarValue, long lTotalBytesSent); 
     private void ThreadSafe_UpdateProgressBar(int iProgressBarValue, long lTotalBytesSent) 
     { 
      if (this.InvokeRequired) 
      { 
       ThreadSafe_UpdateProgressBar_Callback d = 
        new ThreadSafe_UpdateProgressBar_Callback(ThreadSafe_UpdateProgressBar); 
       try 
       { 
        this.Invoke(d, new object[] { iProgressBarValue, lTotalBytesSent }); 
       } 
       catch 
       { 
        //ObjectDisposedException 
       } 
      } 
      else 
      { 
       //Send the call on to the normal function 
       ThreadSafe_UpdateProgressBar_Proc(iProgressBarValue, lTotalBytesSent); 
      } 
     } 
     private void ThreadSafe_UpdateProgressBar_Proc(int iProgressBarValue, long lTotalBytesSent) 
     { 
      label4.Text = lTotalBytesSent.ToString(); 
      progressBar1.Value = iProgressBarValue; 
     } 
     #endregion 
    } 
} 
+0

沒有必要對中的DoEvents工作者線程中調用,因爲它不會有任何。 GUI將擁有自己的線程。 – Deanna 2012-07-15 12:29:30

+0

我看不到通過以下方式獲取實際上傳進度的方法:| – Deanna 2012-07-15 12:45:02

+0

是啊迪安娜嘿嘿,你和喬伊兩人都讓我對這些的DoEvents地獄......我恨他們也和不使用它們!這只是我試圖測試的東西,我已經瞭解到,如果一個工作線程返回上傳進度(將更新事件發送到主線程),它使主線程沒有時間更新。如果我在工作線程中有doevents,它似乎會減慢它的速度,使主線程趕上「動畫」進度條。無論如何,這只是一個考驗。我可以很好地使用Thread.Sleep(500)。 ;)所以沒有解決方案吧。 :/ – 2012-07-15 22:16:25

回答

1

人爲設定的速度一樣,不會有一個慢速鏈接的效果相同。限制在客戶端限制從緩衝區中讀取的數量,但實際上不會改變它發送的速率,直到兩端的緩衝區被填滿爲止。

如果您嘗試上傳一個更大的文件,你應該可以看到上傳失速,一旦他們填補了緩衝區,那麼它的預期收益率進行。

.Write()會阻塞,直到它被寫入套接字,並且如果您希望它是異步的,則需要使用異步.BeginWrite()調用。

+0

這是問題所在。我甚至創建了自己的ftp類,並使用套接字來代替Microsoft封裝的本地ftp庫。如果服務器限制帶寬(在您的服務器上),發送小文件,並在服務器上設置一個用於文件傳輸的大緩衝區,那麼您將遇到此問題。 – 2012-07-22 20:41:09

0

爲什麼不使用背景工人來處理進度,那麼如果你想讓它等待,那麼它就是不會妨礙你的主線程。

+0

咦?我的主線不會陷入困境......我使用後臺工作線程完成繁重的工作,進度條由主線程更新......只要讓主線程呼吸一點,不要錘擊它(你永遠不應該這樣做)一切正常。 – 2012-07-17 00:56:20