c#无法打开文件进行读取

c#无法打开文件进行读取,c#,C#,我正在编写一个程序,它使用FileSystemWatcher监视对给定目录的更改,当它接收到OnCreated或OnChanged事件时,它会将那些创建/更改的文件复制到指定目录。起初,我对OnChanged/OnCreated事件可以发送两次(如果需要处理500MB文件,则不可接受)这一事实存在问题,但我设法解决了这一问题,我真正被阻止的是获得以下IOException: 进程无法访问文件“C:\Where are Photos\bookmarks(11).html”,因为另一个进程正在使用该

我正在编写一个程序,它使用
FileSystemWatcher
监视对给定目录的更改,当它接收到OnCreated或OnChanged事件时,它会将那些创建/更改的文件复制到指定目录。起初,我对OnChanged/OnCreated事件可以发送两次(如果需要处理500MB文件,则不可接受)这一事实存在问题,但我设法解决了这一问题,我真正被阻止的是获得以下
IOException
: 进程无法访问文件“C:\Where are Photos\bookmarks(11).html”,因为另一个进程正在使用该文件

因此,防止程序复制它应该复制的所有文件。 正如我提到的,当用户使用这个程序时,他/她指定了监控目录,当用户复制/创建/更改该目录中的文件时,程序应该得到OnCreated/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);
}
一旦更改:

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);
    }
}
副本:

这是工作,因为我可以告诉(测试),但。。。这是黑客,更不用说其他的缺陷了

我可以尝试的另一个“解决方案”(我还没有测试它)是在
fileType()
方法末尾的某处使用
GC.Collect()
。也许比上一个更糟糕的“解决方案”

有人能告诉我,到底是什么在锁定文件,阻止它打开,我该如何修复?我错过了什么


提前感谢。

问题很可能是您已经尝试访问该文件,但仍在复制该文件。这种情况可能会发生,尤其是在大型文件上

在实际开始处理之前,您可以尝试检查是否可以使用写入权限打开该文件。有关如何执行该检查的详细信息


如果您可以影响创建文件的过程,可能会有更好的解决方案。首先使用临时扩展名复制文件,然后在复制完成后重命名文件,以便触发
FileSystemWatcher
事件。

您可以尝试使用卷影复制。
有关详细信息,请参见www.codeproject.com/KB/dotnet/makeshadowcopy.aspx。

文件系统监视程序事件在文件开始复制时触发,而不是在文件结束时触发,因此经常会遇到此类错误

但是,您的第一种方法是可行的,我建议在另一个线程上旋转I/O密集型代码,并使用增量Sleep()而不是繁忙的等待


但是,如果您可以访问实际创建文件的软件,那么更改扩展名是一个稍微不那么复杂的解决方案。请注意,
FileSystemwatcher
上的
xls
过滤器将匹配名为
myfile1.xls.temp
的文件,因为我发现了困难的方法:)

非常感谢快速响应,但我不需要写权限,我需要读取权限,以便可以从中读取文件并将其复制到其他目标。@Maks:当您可以写入文件时,您也可以读取它。如果您永远无法写入,例如,因为文件夹受到保护,您也可以尝试以只读方式打开文件。我的意思是,如果是windows进程(复制)而不是写入权限(因为在事件发生时未完成),则获取读取权限可能“更容易”(可能比写入权限更容易):)你的解决方案看起来有点像我的第一个“解决方案”。如果我找不到更好(更干净)的东西,我会用类似的东西。再次感谢:)谢谢斯维科。正如我所注意到的,FSW在文件写入(0字节)时触发,然后在复制过程结束时第二次触发,因为文件大小将从0更改为原始大小。我想我需要使用第一种方法,当然需要一些延迟时间。如果我找不到锁定文件的原因以及如何解决这个问题,这是我最后的办法。
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);
    }
}
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;
}