我正在编写一个使用FileSystemWatcher
监视给定目录更改的程序,当它收到OnCreated或OnChanged事件时,会将这些创建/更改的文件复制到指定目录。起初我遇到的问题是OnChanged / OnCreated事件可以发送两次(如果它需要处理500MB文件则不可接受),但是我绕过了这个并且我真正被阻止的是得到以下{ {1}}:
该进程无法访问文件'C:\ Where Photos \ bookmarks(11).html',因为它正由另一个进程使用。
因此,阻止程序复制它应该的所有文件。 正如我所提到的,当用户使用该程序时,他/她指定了受监控的目录,当用户在该目录中复制/创建/更改文件时,程序应该获得OnCreated / OnChanged事件,然后将该文件复制到其他几个目录中。 在所有情况下都会出现上述错误,如果用户复制的文件很少需要覆盖被监视文件夹中的其他文件,或者复制大量多个文件,甚至有时复制受监控目录中的一个文件。 整个程序非常大,所以我发送最重要的部分。 OnCreated:
IOException
调用onChanged:
private void OnCreated(object source, FileSystemEventArgs e) {
AddLogEntry(e.FullPath, "created", "");
// Update last access data if it's file so the same file doesn't
// get processed twice because of sending another event.
if (fileType(e.FullPath) == 2) {
lastPath = e.FullPath;
lastTime = DateTime.Now;
}
// serves no purpose now, it will be remove soon
string fileName = GetFileName(e.FullPath);
// copies file from source to few other directories
Copy(e.FullPath, fileName);
Console.WriteLine("OnCreated: " + e.FullPath);
}
的fileType:
private void OnChanged(object source, FileSystemEventArgs e) {
// is it directory
if (fileType(e.FullPath) == 1)
return; // don't mind directory changes itself
// Only if enough time has passed or if it's some other file
// because two events can be generated
int timeDiff = ((TimeSpan)(DateTime.Now - lastTime)).Seconds;
if ((timeDiff < minSecsDiff) && (e.FullPath.Equals(lastPath))) {
Console.WriteLine("-- skipped -- {0}, timediff: {1}", e.FullPath, timeDiff);
return;
}
// Update last access data for above to work
lastPath = e.FullPath;
lastTime = DateTime.Now;
// Only if size is changed, the rest will handle other handlers
if (e.ChangeType == WatcherChangeTypes.Changed) {
AddLogEntry(e.FullPath, "changed", "");
string fileName = GetFileName(e.FullPath);
Copy(e.FullPath, fileName);
Console.WriteLine("OnChanged: " + e.FullPath);
}
}
拷贝:
private int fileType(string path) {
if (Directory.Exists(path))
return 1; // directory
else if (File.Exists(path))
return 2; // file
else
return 0;
}
我在我的程序中到处检查,每当我使用某个文件时,我在private void Copy(string srcPath, string fileName) {
foreach (string dstDirectoy in paths) {
string eventType = "copied";
string error = "noerror";
string path = "";
string dirPortion = "";
// in case directory needs to be made
if (srcPath.Length > fsw.Path.Length) {
path = srcPath.Substring(fsw.Path.Length,
srcPath.Length - fsw.Path.Length);
int pos = path.LastIndexOf('\\');
if (pos != -1)
dirPortion = path.Substring(0, pos);
}
if (fileType(srcPath) == 1) {
try {
Directory.CreateDirectory(dstDirectoy + path);
//Directory.CreateDirectory(dstDirectoy + fileName);
eventType = "created";
} catch (IOException e) {
eventType = "error";
error = e.Message;
}
} else {
try {
if (!overwriteFile && File.Exists(dstDirectoy + path))
continue;
// create new dir anyway even if it exists just to be sure
Directory.CreateDirectory(dstDirectoy + dirPortion);
// copy file from where event occured to all specified directories
using (FileStream fsin = new FileStream(srcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
using (FileStream fsout = new FileStream(dstDirectoy + path, FileMode.Create, FileAccess.Write)) {
byte[] buffer = new byte[32768];
int bytesRead = -1;
while ((bytesRead = fsin.Read(buffer, 0, buffer.Length)) > 0)
fsout.Write(buffer, 0, bytesRead);
}
}
} catch (Exception e) {
if ((e is IOException) && (overwriteFile == false)) {
eventType = "skipped";
} else {
eventType = "error";
error = e.Message;
// attempt to find and kill the process locking the file.
// failed, miserably
System.Diagnostics.Process tool = new System.Diagnostics.Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = "\"" + srcPath + "\"";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern)) {
System.Diagnostics.Process.GetProcessById(int.Parse(match.Value)).Kill();
}
Console.WriteLine("ERROR: {0}: [ {1} ]", e.Message, srcPath);
}
}
}
AddLogEntry(dstDirectoy + path, eventType, error);
}
}
块中使用它,所以甚至将事件写入log(我省略的类,因为已经有太多代码已经在帖子中)不会锁定该文件,因为所有操作都使用using
语句块。
如果不是我的程序“复制”进程从用户通过Windows或其他东西,我根本不知道是谁锁定文件。
现在我有两个可能的“解决方案”(我不能说它们是干净的解决方案,因为它们是黑客并且因此不可取)。因为问题可能是using
方法(还有什么可以锁定文件?)我尝试将其更改为此,以模拟“阻塞直到准备打开”操作:
的fileType:
fileType
这是我能告诉的(测试),但是......它是黑客,更不用说其他缺陷了。
我可以尝试的其他“解决方案”(我还没有测试过)在private int fileType(string path) {
FileStream fs = null;
int ret = 0;
bool run = true;
if (Directory.Exists(path))
ret = 1;
else {
while (run) {
try {
fs = new FileStream(path, FileMode.Open);
ret = 2;
run = false;
} catch (IOException) {
} finally {
if (fs != null) {
fs.Close();
fs.Dispose();
}
}
}
}
return ret;
}
方法的末尾某处使用GC.Collect()
。也许比以前更糟糕的“解决方案”。
有人请求告诉我,究竟是什么锁定文件,阻止它打开,我该如何解决?我错过了什么?
提前致谢。
答案 0 :(得分:2)
问题很可能是您在尝试访问该文件时仍在复制该文件。特别是在大文件上会发生这种情况。
您可以尝试在实际开始处理之前检查是否可以使用写入权限打开文件。有关如何执行此操作的详细信息,请检查here。
如果您可以影响创建文件的过程,则可能有更好的解决方案。首先使用临时扩展名复制文件,然后在复制完成后重命名该文件,以便触发FileSystemWatcher
事件。
答案 1 :(得分:1)
您可以尝试使用卷影副本。 有关详细信息,请访问www.codeproject.com/KB/dotnet/makeshadowcopy.aspx。
答案 2 :(得分:1)
FileSystemWatcher事件在文件开始复制时触发,而不是在结束时触发,因此遇到这种错误很常见。
您的第一种方法是可行的,但是,我建议在另一个线程上旋转I / O密集型代码,并使用增量Sleep()而不是忙碌的等待。
但是,如果您可以访问实际创建文件的软件,则扩展更改是一个稍微复杂的解决方案。请注意,xls
上的FileSystemwatcher
过滤器会匹配名为myfile1.xls.temp
的文件,因为我发现了困难的方法:)