Вот оптимизированное решение, использующее расширения .NET и двойной буфер для повышения производительности.Новая перегрузка CopyTo добавлена в FileInfo с действием, которое показывает прогресс только после его изменения.
Этот пример реализации в WPF с индикатором выполнения с именем progressBar1, который выполняет операцию копирования в фоновом режиме.
private FileInfo _source = new FileInfo(@"C:\file.bin");
private FileInfo _destination = new FileInfo(@"C:\file2.bin");
private void CopyFile()
{
if(_destination.Exists)
_destination.Delete();
Task.Run(()=>{
_source.CopyTo(_destination, x=>Dispatcher.Invoke(()=>progressBar1.Value = x));
}).GetAwaiter().OnCompleted(() => MessageBox.Show("File Copied!"));
}
Вот пример для Консольного приложения
class Program
{
static void Main(string[] args)
{
var _source = new FileInfo(@"C:\Temp\bigfile.rar");
var _destination = new FileInfo(@"C:\Temp\bigfile2.rar");
if (_destination.Exists) _destination.Delete();
_source.CopyTo(_destination, x => Console.WriteLine($"{x}% Complete"));
Console.WriteLine("File Copied.");
}
}
Чтобы использовать, создайте новый файл, например FileInfoExtensions.cs, и добавьте этот код:
public static class FileInfoExtensions
{
public static void CopyTo(this FileInfo file, FileInfo destination, Action<int> progressCallback)
{
const int bufferSize = 1024 * 1024; //1MB
byte[] buffer = new byte[bufferSize], buffer2 = new byte[bufferSize];
bool swap = false;
int progress = 0, reportedProgress = 0, read = 0;
long len = file.Length;
float flen = len;
Task writer = null;
using (var source = file.OpenRead())
using (var dest = destination.OpenWrite())
{
dest.SetLength(source.Length);
for (long size = 0; size < len; size += read)
{
if ((progress = ((int)((size / flen) * 100))) != reportedProgress)
progressCallback(reportedProgress = progress);
read = source.Read(swap ? buffer : buffer2, 0, bufferSize);
writer?.Wait(); // if < .NET4 // if (writer != null) writer.Wait();
writer = dest.WriteAsync(swap ? buffer : buffer2, 0, read);
swap = !swap;
}
writer?.Wait(); //Fixed - Thanks @sam-hocevar
}
}
}
двойной буфер работает, используя один поток для чтения и один поток для записи, поэтому максимальная скорость определяется только более медленной из двух.Используются два буфера (двойной буфер), гарантирующий, что потоки чтения и записи никогда не будут использовать один и тот же буфер в одно и то же время.
Пример: код считывает данные в буфер 1, затем, когда чтение завершается, aОперация записи начинает запись содержимого буфера 1. Не дожидаясь окончания записи, буфер переключается на буфер 2, и данные считываются в буфер 2, пока буфер 1 все еще записывается.Как только чтение завершается в буфере 2, оно ожидает завершения записи в буфере 1, начинает запись в буфер 2, и процесс повторяется.По сути, 1 поток всегда читает, а другой всегда пишет.
WriteAsync использует перекрывающийся ввод / вывод , который использует порты завершения ввода / вывода , которые полагаются на аппаратное обеспечение для выполнения асинхронных операций, а не на потоки, что делает это очень эффективным.TLDR: я соврал, что есть 2 потока, но концепция та же самая.