【问题标题】:Stream.CopyTo - How do I get the sent Bytes?Stream.CopyTo - 如何获取发送的字节?
【发布时间】:2015-02-18 23:21:06
【问题描述】:


我尝试在 ftp 上传时获得传输速度,但我不知道应该从哪里“获取”它:

代码片段:

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();
}

我已经读过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);
    }
}

不是很有效,根据留下的the comment

请注意,这不是最快的方法。在提供的代码 sn-p 中,您必须等待写入完成才能读取新块。当异步执行读取和写入时,这种等待将消失。在某些情况下,这会使复制速度提高一倍。但是它会使代码变得更加复杂,所以如果速度不是问题,请保持简单并使用这个简单的循环。

如何在 chrome 或 firefox 上显示传输速度?


编辑:
这是我在您(Tien Dinh)回答之前尝试过的:
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();

如您所见,有一个 BackgroundWorker,因此我使用 CopyToAsync。

【问题讨论】:

    标签: c# ftp file-transfer


    【解决方案1】:

    您可以构建自己的流包装类,报告在定义的时间间隔内写入的字节数:

    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();
            }
        }
    }
    

    你会这样使用它:

    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);
    }
    

    BackgroundWorker 将以当前进度和速度重复调用。您可以使用存储最近 n 个速度并报告平均值的队列来优化该示例。

    【讨论】:

    • 这非常棒,但对我来说太难了。你能再解释一下吗`double speed = this.bytesInLastInterval/(this.stopwatch.Elapsed.Ticks/(double) this.interval.Ticks);`
    • 所以速度是一个等式,我也许可以在我的代码中使用它,您可以在我的编辑中看到它?或者input.positioninput.length 不可能吗?
    • 每次向流中写入内容时,我们都会跟踪字节数。一旦我们的秒表超过我们的间隔,我们就会计算速度。示例:间隔 = 1 秒。秒表 = 1.1 秒。 BytesInLastInterval = 1100。显然,我们在 1.1 秒内制作了 1100 个字节,因此我们得到 1100 / (1.1 / 1.0) = 1000 个字节/秒。
    • 好的。我理解这个等式,但有太多我什至不知道的代码。有没有更简单的方法?例如,如果我使用 Stream.CopyTo 我可以得到一个时间跨度,我不能吗?我猜不会。如果我使用 Tien Dinh 发布的代码,我可以在那里使用方程式吗?我也猜不到?!还是我误解了什么?
    • @Ismoh 大部分代码只是将调用转发到 innerStream 实例。这是装饰器模式的一个应用。它所做的是包装一个常规 Stream(例如您的 FileStream 实例)并拦截对它的所有调用。这样,我们可以在调用 Write 或 WriteBytes 时调用自己的方法 (UpdateSpeed)。
    【解决方案2】:

    你已经有一个 CopyStream 方法,只需要提高性能。 BufferedStream 非常适合这个。见下文。

    我相信您还可以通过使用 .net 4 中的 Async 方法进一步改进它。

    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();
    }
    

    【讨论】:

    • Action&lt;int&gt; totalSent 对我来说是新的。它是如何工作的?那是一种方法吗?那么totalSent(total) 呢?那是发送的数据量吗?我怎样才能得到类似“12345bytes/s”的东西?你的 code-sn-p 比CopyTo好吗?很抱歉所有这些问题,但我对某些事情还是陌生的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 1970-01-01
    • 2018-06-26
    相关资源
    最近更新 更多