计算文件复制的剩余时间

时间:2014-02-19 12:22:03

标签: c# math file-copying

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

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

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

double bytePerSec = totalBytesCopied / TimeTaken.TotalSeconds;

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

double secRemain = (totalFileSizeToCopy - totalBytesCopied) / bytePerSec;

我认为,一旦复制了几MB,剩余的时间会变得更加稳定(虽然期望它会改变。但事实并非如此,它的不稳定和跳跃遍布整个地方。

然后我在SO上尝试了一个解决方案......

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

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

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

4 个答案:

答案 0 :(得分:5)

以下是一个工作示例,说明如何将文件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();
        }
    }
}

答案 1 :(得分:1)

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

答案 2 :(得分:1)

这可能是一个方法

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

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

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

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

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

答案 3 :(得分:1)

您希望根据平均传输速率计算速率,但您希望它是移动平均值,因为网络速度在文件传输的生命周期内是可变的(特别是对于非常大的文件)。这是我提出的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是转移的总金额,包括当前的卡盘。

我发现像这样的加权平均值具有良好的准确性,但也对速度的变化做出了很好的反应。