Каков правильный способ копирования или перемещения файла в С#?
Асинхронное копирование файлов/перемещение в С#
Ответ 1
Идея асинхронного программирования состоит в том, чтобы позволить вызывающему потоку (при условии, что он является потоком пула потоков) возвращаться в пул потоков для использования в какой-либо другой задаче, в то время как async IO завершается. Под капотом контекст вызова заполняется в структуру данных, и один или несколько потоков завершения ввода-вывода контролируют ожидающий завершения вызов. Когда IO завершает поток завершения, возвращается обратно в пул потоков, восстанавливая контекст вызова. Таким образом, вместо 100 блокировок потоков есть только потоки завершения и несколько потоков пулов потоков, которые почти не работают.
Лучшее, что я могу придумать, это:
public async Task CopyFileAsync(string sourcePath, string destinationPath)
{
using (Stream source = File.Open(sourcePath))
{
using(Stream destination = File.Create(destinationPath))
{
await source.CopyToAsync(destination);
}
}
}
Однако я не проводил обширного тестирования на этом. Я немного волнуюсь, потому что, если бы это было так просто, это уже было бы в основных библиотеках.
Ожидание делает то, что я описываю за кулисами. Если вы хотите получить общее представление о том, как это работает, это, вероятно, поможет понять Джеффа Рихтера AsyncEnumerator. Они могут быть не совсем одинаковыми линиями, но идеи действительно близки. Если вы когда-либо смотрите на стек вызовов из метода "async", вы увидите MoveNext на нем.
Что касается перемещения, ему не нужно быть асинхронным, если это действительно "Переместить", а не копировать, а затем удалить. Перемещение - это быстрая атомная операция против таблицы файлов. Это работает только так, но если вы не пытаетесь переместить файл в другой раздел.
Ответ 2
Здесь используется метод копирования асинхронного файла, который дает подсказки ОС, которые мы читаем и записываем последовательно, чтобы он мог предварительно извлекать данные для чтения и готовить все для записи:
public static async Task CopyFileAsync(string sourceFile, string destinationFile)
{
using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
using (var destinationStream = new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
await sourceStream.CopyToAsync(destinationStream);
}
Вы также можете поэкспериментировать с размером буфера. Вот он 4096 байт.
Ответ 3
Я немного улучшил код с помощью @DrewNoakes (производительность и отмена):
public static async Task CopyFileAsync(string sourceFile, string destinationFile, CancellationToken cancellationToken)
{
var fileOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
var bufferSize = 4096;
using (var sourceStream =
new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, fileOptions))
using (var destinationStream =
new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize, fileOptions))
await sourceStream.CopyToAsync(destinationStream, bufferSize, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
}
Ответ 4
Вы можете использовать асинхронные делегаты
public class AsyncFileCopier
{
public delegate void FileCopyDelegate(string sourceFile, string destFile);
public static void AsynFileCopy(string sourceFile, string destFile)
{
FileCopyDelegate del = new FileCopyDelegate(FileCopy);
IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null);
}
public static void FileCopy(string sourceFile, string destFile)
{
// Code to copy the file
}
public static void CallBackAfterFileCopied(IAsyncResult result)
{
// Code to be run after file copy is done
}
}
Вы можете назвать это как:
AsyncFileCopier.AsynFileCopy("abc.txt", "xyz.txt");
Ответ 5
Вы можете сделать это как this, предложенная статьей:
public static void CopyStreamToStream(
Stream source, Stream destination,
Action<Stream, Stream, Exception> completed)
{
byte[] buffer = new byte[0x1000];
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);
Action<Exception> done = e =>
{
if(completed != null) asyncOp.Post(delegate
{
completed(source, destination, e);
}, null);
};
AsyncCallback rc = null;
rc = readResult =>
{
try
{
int read = source.EndRead(readResult);
if(read > 0)
{
destination.BeginWrite(buffer, 0, read, writeResult =>
{
try
{
destination.EndWrite(writeResult);
source.BeginRead(
buffer, 0, buffer.Length, rc, null);
}
catch(Exception exc) { done(exc); }
}, null);
}
else done(null);
}
catch(Exception exc) { done(exc); }
};
source.BeginRead(buffer, 0, buffer.Length, rc, null);
Ответ 6
В то время как есть некоторые обстоятельства, когда вы хотите избежать Task.Run
, Task.Run(() => File.Move(source, dest)
будет работать. Это стоит учитывать, потому что, когда файл просто перемещается на том же диске/томе, это почти мгновенная операция, так как заголовки изменены, но содержимое файла не перемещается. Различные "чистые" асинхронные методы неизменно копируют поток, даже если нет необходимости делать это, и в результате на практике может быть немного медленнее на практике.
Ответ 7
AFAIK, асинхронный API высокого уровня не копирует файл. Однако вы можете создать свой собственный API для выполнения этой задачи с помощью API Stream.BeginRead/EndRead
и Stream.BeginWrite/EndWrite
. В качестве альтернативы вы можете использовать метод BeginInvoke/EndInvoke
, как указано в ответах здесь, но вы должны иметь в виду, что они не будут блокировать асинхронный ввод-вывод. Они просто выполняют задачу в отдельном потоке.
Ответ 8
Правильный способ копирования: использовать отдельный поток.
Вот как вы могли бы это сделать (синхронно):
//.. [code]
doFileCopy();
// .. [more code]
Здесь, как сделать это асинхронно:
// .. [code]
new System.Threading.Thread(doFileCopy).Start();
// .. [more code]
Это очень наивный способ сделать что-то. Совершенно хорошо, решение будет включать в себя некоторый метод event/delegate для отчета о статусе копии файла и уведомления о важных событиях, таких как сбой, завершение и т.д.
веселит, JRH
Ответ 9
Я бы предположил, что функция File Copy IO, доступная на языках программирования .Net, в любом случае является асинхронной. После использования его в моей программе для перемещения небольших файлов, кажется, что последующие инструкции начинают выполняться до завершения фактической копии файла. Я разбираюсь в том, что исполняемый файл дает Windows задачу сделать копию, а затем сразу же возвращается для выполнения следующей инструкции - не дожидаясь завершения Windows. Это заставляет меня конструировать в цикле сразу после вызова для копирования, который будет выполняться, пока я не смогу подтвердить, что копия завершена.