2012-07-14 74 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