问题
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.
回答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.
回答2:
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();
}
来源:https://stackoverflow.com/questions/27575933/stream-copyto-how-do-i-get-the-sent-bytes