如何等待文件从网络驱动器成功复制后再读取?



我目前正面临一个由多个组件组成的应用程序的问题。应用程序的一个组件定期检查网络驱动器上的新文件,并将它们复制到本地文件夹中。应用程序的另一个组件使用FileSystemWatcher监视本地文件夹中的任何新文件。如果复制了一个新文件,则调用FileSystemWatcherCreated事件,然后应用程序将读取文件内容并将文件导入数据库。为了防止应用程序在将文件完全复制到本地文件夹之前尝试读取文件,它定期调用以下函数,直到返回false:

private bool isFileLocked(string filePath)
{
try
{
if (!File.Exists(filePath))
{
return false;
}
using (FileStream fs = File.OpenRead(filePath))
{
}
return false;
}
catch (IOException)
{
return true;
}
}

不幸的是,这似乎不是在所有情况下都有效。有时,我注意到文件在完全写入本地文件夹之前就被读取了。当发生这种情况时,试图复制文件的组件会得到以下错误:

System.IO.IOException: The process cannot access the file '...' because it is being used by another process.

复制文件的组件是在PowerShell中编写的,并使用以下Cmdlet进行复制:

Copy-Item $currentfile.FullName -Destination "$destfolder" –Force -ErrorAction Stop

使用FileSystemWatcher并导入文件的组件是一个基于c#的windows服务。如何防止它在文件完全复制到本地文件夹之前读取文件?

如果你不担心小小的延迟——它可以解决你的麻烦:

static void Main(string[] args)
{
FileSystemWatcher fsw = new FileSystemWatcher("SomePathToFolder");
fsw.EnableRaisingEvents = true;
fsw.Created += async (s, a) =>
{
while (FileIsLocked(a.FullPath))
{
Console.WriteLine($"File {a.Name} is locked!");
await Task.Delay(TimeSpan.FromSeconds(5)); // 5 seconds delay between checks
}

Console.WriteLine($"File {a.Name} available!");

// You can put here another delay to be 102% sure that file is free,
// but I suppose this is too much.
using (FileStream fs = File.OpenRead(a.FullPath))
{
Console.WriteLine($"File {a.Name} opened for reading.");
// Do what you need
await Task.Run(() => ImportFileToDatabase(fs));
}
Console.WriteLine($"File {a.Name} closed.");
};

Console.ReadKey();
}
static bool FileIsLocked(string filePath)
{
if (!File.Exists(filePath))
return false;
try
{
using (FileStream fs = File.OpenRead(filePath)) { }
return false;
}
catch { }
return true;
}

这里提出了一些解决方案。我有一个类似的问题使用FileSystemWatcher。这是我使用的(简化):

async Task<FileStream> OpenWaitAsync(string path, TimeSpan interval, CancellationToken cancellationToken = default)
{
const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
while (true)
{
try
{
return File.OpenRead(path);
}
catch (IOException ioe) when (ioe.HResult == ERROR_SHARING_VIOLATION)
{
await Task.Delay(interval, cancellationToken);
}
}
}

相关内容

最新更新