【问题标题】:Calculating Time Remaining on File Copy计算文件复制的剩余时间
【发布时间】:2014-03-19 18:37:03
【问题描述】:

我有一个应用程序可以通过网络将大量文件复制到文件服务器(不是网络)。我正在尝试显示对剩余时间的一半体面估计。

我查看了许多关于 SO 的文章,但问题得到了解决,但我没有尝试真正做我想做的事。我希望估计的剩余时间相对稳定。不要根据波动的传输速度到处乱跳。

所以我看到的第一个解决方案是以每秒字节数为单位计算传输速度

double bytePerSec = totalBytesCopied / TimeTaken.TotalSeconds;

然后将剩余的总字节数除以传输速率。

double secRemain = (totalFileSizeToCopy - totalBytesCopied) / bytePerSec;

我认为一旦复制了几 MB,剩余的时间会变得更加稳定(尽管预计它会改变。它没有,它不稳定并且到处乱跳。

然后我在 SO.... 上尝试了其中一种解决方案。

double secRemain = (TimeTaken.TotalSeconds / totalBytesCopied) * (totalFileSizeToCopy - totalBytesCopied);

这是一个类似的计算,但希望它可能会有所作为!

所以现在我有点想我需要从不同的角度来解决这个问题。 IE 使用平均值?使用某种倒数计时器并每隔一段时间重新设置一次时间?只是寻找已经遇到此问题的任何人的意见或最好的建议。

【问题讨论】:

  • 您可能还想考虑每个文件可能带有每个文件的延迟时间 - 用于打开流并在某处创建新文件。复制一百万个 1kB 文件会比复制一个 1 GB 文件花费更长的时间吗?
  • 您是否可以显示带有“正在复制 XXX 的 1 个”的进度条?
  • link 这是你可能想要的。
  • @ShellShock 绝对是的,但必须有一种方法来计算并稳定结果,以便粗略估计总传输需要多长时间。
  • 假设您的网络硬件在您开始传输时已经处于最大负载下,并且公式表明完成文件传输需要 2 小时。现在,在前 10 分钟之后,占用您资源的进程退出了,看起来您的文件传输的其余部分只需要 5 分钟。您希望计时器指示什么?在文件传输 20、30、40... 分钟内重复相同的思维实验。

标签: c# math file-copying


【解决方案1】:

您可能希望根据平均传输率计算估算值。这应该有助于稳定估计。 another SO question 提出了同样的问题。

【讨论】:

    【解决方案2】:

    这可能是一种方法

    计算第一个文件的 25% 已被复制时的字节数/秒

    使用该计算来估计第一个文件操作的 75% 的其余部分

    每次复制文件时,都会在集合中存储每个复制文件的平均速度(以字节/秒为单位)。

    然后你可以使用集合中项目的平均值来估计尚未复制的文件的复制操作的时间。

    如果您需要更多详细信息或方法,请告诉我

    【讨论】:

      【解决方案3】:

      您希望根据平均传输速率计算速率,但您希望它是一个移动平均值,因为网络速度在文件传输的整个生命周期内是可变的(尤其是对于非常大的文件)。这是我想出的似乎运行良好的 JavaScript 方法(应该很容易转换为 C#)。

      var rates = [];
      var ratesLength = 1000;
      for (var i = 0; i < ratesLength; i++)
          rates[i] = 0;
      
      /**
      * Estimates the remaining download time of the file.
      *
      * @param {number} bytesTransferred: Number of bytes transferred so far.
      * @param {number} totalBytes: Total number of bytes to be transferred.
      * @return {string} Returns the estimating time remaining in the upload/download.
      */
      function getSpeed(bytesTransferred, totalBytes) {
      
          var bytes = bytesTransferred - oldBytesTransfered;
          var time = currentTime - oldTime;
      
          if ((time != 0) && (bytes != 0)) {
              rates[rateIndex] = (bytes) / (time);
              rateIndex = (rateIndex + 1) % rates.length;
              var avgSpeed = 0;
              var count = 0;
              for (i = 0; i < rates.length ; i++) {
                  if (rates[i] != 0) {
                      avgSpeed += rates[i];
                      count++;
                  }
              }
              if (count == 0)
                  return " ";
      
              avgSpeed /= count;
              return (humanReadableTime((totalBytes - bytesTransferred) / avgSpeed) + " remaining");
          } else {
              return " ";
          }
      }
      

      您可以调整ratesLength 以获得您想要的平滑度。 oldTime 是接收到最后一个字节的时间,oldBytesTransfered 是在最后一个块之后传输的总字节数。 bytesTransferred 是包括当前卡盘在内的总转移量。

      我发现像这样进行加权平均具有很好的准确性,而且对速度的变化也有很好的反应。

      【讨论】:

        【解决方案4】:

        这是一个工作示例,说明如何将文件 D:\dummy.bin 异步复制到 D:\dummy.bin.copy,并使用计时器每秒拍摄传输速率的快照。

        从该数据中,我只需获取最多 30 个快照的平均传输速率(最新的优先)。从中我可以粗略估计传输文件的其余部分需要多长时间。

        此示例按原样提供,不支持在 1 次操作中复制多个文件。但它应该会给你一些想法。

        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.Linq;
        using System.Threading;
        
        public class Program
        {
            public static void Main(string[] args)
            {
                var sourcePath = @"D:\dummy.bin";
                var destinationPath = @"D:\dummy.bin.copy";
                var sourceFile = new FileInfo(sourcePath);
                var fileSize = sourceFile.Length;
                var currentBytesTransferred = 0L;
                var totalBytesTransferred = 0L;
                var snapshots = new Queue<long>(30);
                var timer = new System.Timers.Timer(1000D);
                timer.Elapsed += (sender, e) =>
                {
                    // Remember only the last 30 snapshots; discard older snapshots
                    if (snapshots.Count == 30)
                    {
                        snapshots.Dequeue();
                    }
        
                    snapshots.Enqueue(Interlocked.Exchange(ref currentBytesTransferred, 0L));
                    var averageSpeed = snapshots.Average();
                    var bytesLeft = fileSize - totalBytesTransferred;
                    Console.WriteLine("Average speed: {0:#} MBytes / second", averageSpeed / (1024 * 1024));
                    if (averageSpeed > 0)
                    {
                        var timeLeft = TimeSpan.FromSeconds(bytesLeft / averageSpeed);
                        var timeLeftRounded = TimeSpan.FromSeconds(Math.Round(timeLeft.TotalSeconds));
                        Console.WriteLine("Time left: {0}", timeLeftRounded);
                    }
                    else
                    {
                        Console.WriteLine("Time left: Infinite");
                    }
                };
        
                using (var inputStream = sourceFile.OpenRead())
                using (var outputStream = File.OpenWrite(destinationPath))
                {
                    timer.Start();
                    var buffer = new byte[4096];
                    var numBytes = default(int);
                    var numBytesMax = buffer.Length;
                    var timeout = TimeSpan.FromMinutes(10D);
                    do
                    {
                        var mre = new ManualResetEvent(false);
                        inputStream.BeginRead(buffer, 0, numBytesMax, asyncReadResult =>
                        {
                            numBytes = inputStream.EndRead(asyncReadResult);
                            outputStream.BeginWrite(buffer, 0, numBytes, asyncWriteResult =>
                            {
                                outputStream.EndWrite(asyncWriteResult);
                                currentBytesTransferred = Interlocked.Add(ref currentBytesTransferred, numBytes);
                                totalBytesTransferred = Interlocked.Add(ref totalBytesTransferred, numBytes);
                                mre.Set();
                            }, null);
                        }, null);
                        mre.WaitOne(timeout);
                    } while (numBytes != 0);
                    timer.Stop();
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-07-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多