c# - 为什么我在使用 File.Copy() 时看到 IO 异常,该异常似乎来自读取文件?

标签 c# ioexception file-copying

前言:我对编码非常陌生,而且大部分是自学的。

我正在编写一个小应用程序,它会自动将我的游戏保存文件复制到我的备份目录。它使用 FileSystemWatcher 类来监视游戏的保存目录并在我玩游戏时查找更改,并在调整文件大小或写入文件时调用 OnChanged。

我遇到的问题是,当我的程序复制文件时,有时会导致游戏崩溃(在本例中为泰拉瑞亚)。我猜这与访问限制有关,我对此没有太多经验。我很惊讶 File.Copy() 会导致问题,因为它只是读取,但它与泰拉瑞亚一致崩溃。

单步执行 OnChanged 调用表明泰拉瑞亚会写入临时文件,然后将它们复制到实际的保存文件中,并删除临时文件。很常见。

所以这是我的代码以及我所得到的笨拙的解决方法。它使用两个计时器:_delayTimer 在第一次调用 OnChanged 时启动,_canBackupTimer 在 _delayTimer 过去时启动。我在测试过的游戏中没有遇到任何问题。

对于每个Timer,间隔时间为5秒,并且AutoReset = True,所以经过一次后就会停止。

这是我在监控游戏时避免 IO 异常的唯一方法吗?必须有更好的方法,但我不知道该去哪里寻找。我很惊讶 File.Copy 会限制对游戏保存过程的访问。

我应该查看文件访问权限吗?

    private static void _delayTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        _canBackupTimer.Enabled = true;
        _canBackupTimer.Start();
        _delayTimer.Enabled = false;
    }

    private static void _canBackupTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        _lastAutoBackupTime = DateTime.Now;
        _canBackupTimer.Enabled = false;
    }


    private static void OnChanged(object source, FileSystemEventArgs e) {
        while (true) {

            if (!_canBackupTimer.Enabled && !_delayTimer.Enabled && (DateTime.Now - _lastAutoBackupTime).Seconds > 10) {
                //If neither timer is running and 10 seconds 
                //have elapsed since canBackupTimer stopped

                _delayTimer.Enabled = true;
                _delayTimer.Start();
                continue;
            }
            if (_canBackupTimer.Enabled) {
                //if canBackupTimer is running, do autobackup

                Game autoBackupGame = null;

                //_gamesToAutoBackup is a List<Game> for the program to watch.
                //Check to identify which Game is being backed up
                //g.RootFolder is the game's base directory, e.g. "...\Terraria\"
                foreach (var g in _gamesToAutoBackup) { 
                    if (e.FullPath.Contains(g.Name) || e.FullPath.Contains(g.RootFolder))
                        autoBackupGame = g;
                }

                if (autoBackupGame.RootFolder == null) {
                    var dir = new DirectoryInfo(autoBackupGame.Path);
                    autoBackupGame.RootFolder = dir.Name;
                }

                //Find the base directory of the file being changed and trim off
                //the unneeded pieces, e.g. "C:\Users\Rob\..."
                var indexOfGamePart = e.FullPath.IndexOf(autoBackupGame.RootFolder);
                var friendlyPath = e.FullPath.Substring(0, indexOfGamePart);
                var newPath = e.FullPath.Replace(friendlyPath, "\\");


                if (Directory.Exists(e.FullPath) && autoBackupGame != null) {
                    //True if directory, else it's a file.
                    //Do stuff for backing up a directory here.
                    //Currently nothing written here.
                }
                else { //If Directory.Exists is false, the path is a file.

                    try {
                        var copyDestinationFullPath = new FileInfo(_specifiedAutoBackupFolder + newPath);
                        if (!Directory.Exists(copyDestinationFullPath.DirectoryName))
                            Directory.CreateDirectory(copyDestinationFullPath.DirectoryName);
                        File.Copy(e.FullPath, copyDestinationFullPath.ToString(), true);
                    }
                    catch (FileNotFoundException ex) {
                        Logger.Log(ex); //My class, Logger, writes the exception text to a file.
                    }
                }
            }
            break;
        }
    }

最佳答案

我的帖子考虑了有关泰拉瑞亚的评论。我还没有尝试过这个,但如果我想在复制文件的同时防止泰拉瑞亚崩溃,我将采用以下方法解决该问题。

问题

我的猜测是泰拉瑞亚崩溃了,因为当您尝试复制文件时它需要对文件进行写访问。我也因此假设 File.Copy 打开文件没有共享访问权限,因此需要使用不同的机制来打开和复制文件来解决该问题。

解决方案

您需要某种方式来打开和读取文件,同时仍保留泰拉瑞亚同时打开和写入文件的权利。我并不 100% 肯定这种方法可以做到这一点,这取决于泰拉瑞亚尝试打开文件的方式,但您至少可以打开文件,以便仍然允许共享。而不是 File.Copy,请尝试:

using (FileStream inStream = new FileStream(filename, FileMode.Open, 
       FileAccess.Read, FileShare.ReadWrite) {
    // Specifying shared access as FileShare.ReadWrite is the key, as it lets 
    // Terraria open the file with write access.
    // Then add code here to copy the file out to your destination... 
    // this depends a bit on which version of .Net you are using, but 
    // inStream.CopyTo(outStream) is probably the easiest as long as you 
    // are using .Net 4+.
}

结论

如果您的打开失败,或者您的复制失败(异常),或者您的 FileSystemWatcher 在您复制时发出信号,则意味着泰拉瑞亚正在使用该文件,您需要稍后重试。无论如何,希望您能够远离泰拉瑞亚。

我很想知道这种方法是否适合您。如果您尝试一下,请发表评论。

关于c# - 为什么我在使用 File.Copy() 时看到 IO 异常,该异常似乎来自读取文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21736033/

相关文章:

resources - PresentationFramework.dll 中发生类型为 'System.IO.IOException' 的未处理异常

macos - 如何以递归方式复制文件夹并提供进度反馈,同时保留 OS X 上的所有元数据?

bash - 将子目录中的特定文件复制到一个目录中

java - FileUtils.copyFile() VS FileChannel.transferTo()

c# - 如何用 C# 将图像发布到 Twitter

c# - 在 Windows 窗体 c# 中进行拖放操作时,将光标保持在默认形状

c# - WP 运行时组件 - 类型加载异常?

c# - 如何在 UserControl 中使用 ContentPresenter

server - Kafka 服务器无法启动 - java.io.IOException : Map failed