My effort to write async methods for copy/move a file in C#
public static class FileHelper
{
private const int _FileStreamDefaultBufferSize = 4096;
private static bool HasNetworkDrive(string path)
{
try
{
return new DriveInfo(path).DriveType == DriveType.Network;
}
catch (Exception)
{
return false;
}
}
private static bool IsUncPath(string path)
{
try
{
return new Uri(path).IsUnc;
}
catch (Exception)
{
return false;
}
}
private static async Task InternalCopyToAsync(string sourceFilePath, string destFilePath, FileOptions? sourceFileOptions = null, bool overwrite = false)
{
sourceFilePath.AssertHasText(nameof(sourceFilePath));
destFilePath.AssertHasText(nameof(destFilePath));
var sourceStreamFileOpt = (sourceFileOptions ?? FileOptions.SequentialScan) | FileOptions.Asynchronous;
using (FileStream sourceStream = new FileStream(sourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, _FileStreamDefaultBufferSize, sourceStreamFileOpt))
using (FileStream destinationStream = new FileStream(destFilePath, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.Write, FileShare.None, _FileStreamDefaultBufferSize, true))
{
await sourceStream.CopyToAsync(destinationStream, _FileStreamDefaultBufferSize).ConfigureAwait(false);
}
}
public static async Task MoveAsync(string sourceFilePath, string destFilePath)
{
sourceFilePath.AssertHasText(nameof(sourceFilePath));
destFilePath.AssertHasText(nameof(destFilePath));
if (IsUncPath(sourceFilePath) || HasNetworkDrive(sourceFilePath) || IsUncPath(destFilePath) || HasNetworkDrive(destFilePath))
{
await InternalCopyToAsync(sourceFilePath, destFilePath, FileOptions.DeleteOnClose).ConfigureAwait(false);
return;
}
FileInfo sourceFileInfo = new FileInfo(sourceFilePath);
string sourceDrive = Path.GetPathRoot(sourceFileInfo.FullName);
FileInfo destFileInfo = new FileInfo(destFilePath);
string destDrive = Path.GetPathRoot(destFileInfo.FullName);
if (sourceDrive == destDrive)
{
File.Move(sourceFilePath, destFilePath);
return;
}
await Task.Run(() => File.Move(sourceFilePath, destFilePath)).ConfigureAwait(false);
}
public static async Task CopyAsync(string sourceFileName, string destFileName)
{
await InternalCopyToAsync(sourceFileName, destFileName).ConfigureAwait(false);
}
public static async Task CopyAsync(string sourceFileName, string destFileName, bool overwrite)
{
await InternalCopyToAsync(sourceFileName, destFileName, overwrite: overwrite).ConfigureAwait(false);
}
}
The extension method AssertHasText
is just throwing an ArgumentNullException
if !string.IsNullOrWhiteSpace(argument)
Regarding the implementation of MoveAsync
I followed these guidelines
- If the source or dest path are a from a network drive, or UNC path, I call the internal async copy method with the flag
FileOptions.DeleteOnClose
- If the source drive is the same as the dest drive, I call the standard
File.Move
method, because it is an almost-instantaneous operation, as the headers are changed but the file contents are not moved - In any other case, I use a
Task
with the standardFile.Move
. I differentiate the above case to save an unnecessary thread
Question: Regarding the implementation of CopyAsync
it will always copy the stream, Can previous claims be applied to the copy as well?
EDIT:
Adding the implementation of AssertArgumentHasText
public static void AssertArgumentHasText(this string argument, string name)
{
if (argument.IsNullOrEmpty())
{
throw new ArgumentNullException(
name,
string.Format(
CultureInfo.InvariantCulture,
"Argument '{0}' cannot be null or resolve to an empty string : '{1}'.", name, argument));
}
}
string.IsNullOrWhiteSpace
and throws anArgumentNullException
taking the name of the var to pass to the exception as an argument. \$\endgroup\$CopyAsync
method, I updated my question \$\endgroup\$CopyAsync
only in case of Network drive, in other cases you callFile.Move
. So, why do you want to apply the same branching for the Copy? \$\endgroup\$