0


I try to get the transfer speed at a ftp-upload, but I don't know where I should "get" it:

Code-Snippet:

FtpWebRequest request = (FtpWebRequest)WebRequest.Create(job.GetDestinationFolder() + "\\" + fileOnlyName);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(Manager._user, Manager._password);

using (var requestStream = request.GetRequestStream())
{
     using (var input = File.OpenRead(file))
     {
          //input.CopyToAsync()
          input.CopyTo(requestStream);
          //IS HERE ANY METHOD OR ATTRIBUTE, WHICH SHOWS THE SENT BYTES ?
     }
}
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
response.Close();
}

I already read that this code

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    int read;
    while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write (buffer, 0, read);
    }
}

isn't really efficient, according to the comment that was left:

Note that this is not the fastest way to do it. In the provided code snippet, you have to wait for the Write to complete before a new block is read. When doing the Read and Write asynchronously this waiting will disappear. In some situation this will make the copy twice as fast. However it will make the code a lot more complicated so if speed is not an issue, keep it simple and use this simple loop.

How can I show the transfer speed like a download at chrome or firefox ?


EDIT:
This is what I tried before you (Tien Dinh) answered:
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(job.GetDestinationFolder() + "\\" + fileOnlyName);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(Manager._user, Manager._password);

using (var requestStream = request.GetRequestStream())
{
    using (var input = File.OpenRead(file))
    {
        Console.WriteLine(input.Length);//bGroundWorker.ReportProgress(request.)
        Console.WriteLine(input.Position);
        while (input.Position != input.Length)
        {
            input.CopyToAsync(requestStream);
            Console.WriteLine(input.Position);
            //bGroundWorker.ReportProgress( (int) input.Position);
        }
        Console.WriteLine(input.Length + "(length)");
        Console.WriteLine(input.Position + "(sent)");
        //e.Result = input.Position;
    }
}
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
response.Close();

As you can see there is a BackgroundWorker so that I use CopyToAsync.

Community
  • 1
  • 1
Ismoh
  • 1,074
  • 2
  • 12
  • 35

2 Answers2

1

You already have a CopyStream method, just need to improve performance. BufferedStream is great for this. See below.

I believe You can also improve it further by using the Async methods in .net 4.

public static void CopyStream(Stream input, Stream output, Action<int> totalSent)
{
    BufferedStream inputBuffer = new BufferedStream(input);
    BufferedStream outputBuffer = new BufferedStream(output);
    byte[] buffer = new byte[32768];
    int read;
    int total = 0;
    while ((read = inputBuffer.Read(buffer, 0, buffer.Length)) > 0)
    {
         outputBuffer.Write (buffer, 0, read);
         total += read;
         totalSent(total);
    }
    outputBuffer.Flush();
}
Tien Dinh
  • 1,037
  • 7
  • 12
  • Have a look at this article http://www.tsjensen.com/blog/post/2014/01/21/BufferedStream-Improves-NET-Sockets-Performance.aspx – Tien Dinh Dec 20 '14 at 01:22
  • `Action totalSent` is new to me. How does it work? Is that a method? And what about `totalSent(total)`? Is that the amount of sent data? How can I get something like "12345bytes/s"? Is your code-snippet better than `CopyTo`? Sorry for all that questions, but I am still new to certain things. – Ismoh Dec 20 '14 at 01:34
1

You could build your own stream wrapper class that reports the number of bytes written in a defined interval:

public class StreamWithProgress : Stream
{
    private readonly TimeSpan interval;
    private readonly long sourceLength;
    private readonly Stopwatch stopwatch = Stopwatch.StartNew();
    private readonly BackgroundWorker worker;

    private int bytesInLastInterval;
    private long bytesTotal;
    private Stream innerStream;

    public override bool CanRead
    {
        get { return this.innerStream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return this.innerStream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return this.innerStream.CanWrite; }
    }

    public override long Length
    {
        get { return this.innerStream.Length; }
    }

    public override long Position
    {
        get { return this.innerStream.Position; }
        set { this.innerStream.Position = value; }
    }

    public StreamWithProgress(Stream stream, BackgroundWorker worker, long sourceLength, TimeSpan? interval = null)
    {
        if (stream == null)
        {
            throw new ArgumentNullException("stream");
        }

        if (worker == null)
        {
            throw new ArgumentNullException("worker");
        }

        this.interval = interval ?? TimeSpan.FromSeconds(1);
        this.innerStream = stream;
        this.worker = worker;
        this.sourceLength = sourceLength;
    }

    public override void Flush()
    {
        this.innerStream.Flush();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return this.innerStream.Read(buffer, offset, count);
    }

    public override int ReadByte()
    {
        return this.innerStream.ReadByte();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return this.innerStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        this.innerStream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        this.innerStream.Write(buffer, offset, count);
        this.ReportProgress(count);
    }

    public override void WriteByte(byte value)
    {
        this.innerStream.WriteByte(value);
        this.ReportProgress(1);
    }

    protected override void Dispose(bool disposing)
    {
        if (this.innerStream != null)
        {
            this.innerStream.Dispose();
            this.innerStream = null;
        }
    }

    private void ReportProgress(int count)
    {
        this.bytesInLastInterval += count;
        this.bytesTotal += count;

        if (this.stopwatch.Elapsed > this.interval)
        {
            double speed = this.bytesInLastInterval / (this.stopwatch.Elapsed.Ticks / (double) this.interval.Ticks);
            double progress = this.bytesTotal / (double) this.sourceLength;
            var progressPercentage = (int) (progress * 100);

            this.worker.ReportProgress(progressPercentage, speed);

            this.bytesInLastInterval = 0;
            this.stopwatch.Restart();
        }
    }
}

You would use it like this:

BackgroundWorker worker = (BackgroundWorker)sender;
WebRequest request = WebRequest.Create("SOME URL");
WebResponse response = request.GetResponse();

using (Stream stream = response.GetResponseStream())
using (var dest = new StreamWithProgress(File.OpenWrite("PATH"), worker, response.ContentLength))
{
    stream.CopyTo(dest);
}

The BackgroundWorker will be called repeatedly with the current progress and speed. You could refine that example using a queue that stores the last n speeds and reports a mean value.

Frank
  • 4,461
  • 13
  • 28
  • This is pretty much awesome, but too difficult for me atm. Can you explain this a bit further ` double speed = this.bytesInLastInterval/(this.stopwatch.Elapsed.Ticks/(double) this.interval.Ticks);` – Ismoh Dec 20 '14 at 02:04
  • So the speed is a equation which I maybe can use in my code which you can see in my edit ? Or isnt it possible with `input.position` and `input.length` ? – Ismoh Dec 20 '14 at 02:07
  • Every time something is written to the stream, we keep track of the number of bytes. As soon as our stopwatch exceeds our interval, we calculate the speed. Example: Interval = 1 second. Stopwatch = 1.1 seconds. BytesInLastInterval = 1100. Obviously, we made 1100 bytes in 1.1 seconds, so we get 1100 / (1.1 / 1.0) = 1000 bytes per second. – Frank Dec 20 '14 at 02:09
  • Ok. I understand the equation, but there is too much code which I even dont know. Is there a easier way to go ? If I use Stream.CopyTo I can get a timespan for example, couldnt I ? I guess not. If I use the code which Tien Dinh posted, can I use there the equation ? I also guess not ?! OR do I missunderstand something ? – Ismoh Dec 20 '14 at 02:24
  • @Ismoh Most of the code just forwards the calls to the innerStream instance. This is an application of the decorator pattern. What it does is wrap a regular Stream (e.g. your FileStream instance) and intercept all calls to it. This way, we can call our own method (UpdateSpeed) whenever Write or WriteBytes is being called. – Frank Dec 20 '14 at 06:55
  • Yea that's pretty nice. I copied your code and get one error - as already mentioned: I dont understand everything - `protected override void Dispose(bool disposing) { base.Dispose(disposing); if (this.innerStream != null) { this.innerStream.Dispose(); this.innerStream = null; } }` `base.Dispose(disposing);`-> "object didnt have a definition for Dispose". What's wrong here ? – Ismoh Dec 20 '14 at 10:42
  • @Ismoh I'm sorry. Just remove the call to base.Dispose. I have updated my answer. What Dispose does is making sure that the inner stream is closed when you close the wrapper. – Frank Dec 20 '14 at 10:52
  • Sorry- it was still right! I forgot the inheritance `: Stream`... but there is still one error: `await stream.CopyToAsync(dest);` -> "The await-operator can only be used in a async-method. set the method as async and change the return value to TASK." (german to english translation - i hope thats correct) What do you think ? – Ismoh Dec 20 '14 at 10:58
  • Either use CopyTo (synchronously) or add "async" to the calling method: http://msdn.microsoft.com/en-us/library/kztecsys%28v=vs.110%29.aspx – Frank Dec 20 '14 at 11:01
  • The calling-Method is `BackgroundWorker.DoWork(...);`. I thought it's till async. Nevermind. Thanks alot for that detailed help!!! – Ismoh Dec 20 '14 at 11:05
  • It unfortunately do not work neither with `async/await` nor without. The transfer abort instantly without expection or error and the file has an amount of 0Bytes. Any idea ? – Ismoh Dec 20 '14 at 11:15
  • Did you try using source.CopyTo(destination) instead of CopyToAsync? – Frank Dec 20 '14 at 11:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67374/discussion-between-frank-and-ismoh). – Frank Dec 20 '14 at 11:28