为什么使用似乎来自读取文件的File.Copy()看到IO异常? [英] Why am I seeing IO Exception using File.Copy() that seems to come from reading a file?

查看:130
本文介绍了为什么使用似乎来自读取文件的File.Copy()看到IO异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前言:我对编码非常陌生,并且大多是自学成才.

Preface: I'm very new to coding and mostly self-taught.

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

I'm writing a little app that will automatically copy my game save files to my backup directory. It uses the FileSystemWatcher class to watch a game's save directory and look for changes while I'm playing and calls OnChanged if a file is resized or written to.

我遇到的问题是,程序复制文件时,有时会导致GAME(在这种情况下为Terraria)崩溃.我猜想这与访问限制有关,我没有太多经验.我很惊讶File.Copy()会引起问题,因为它只是在读取,但是与Terraria一致崩溃.

The problem I'm having is that when my program is copying a file, it sometimes crashes the GAME (in this case Terraria). I'm guessing this has something to do with access restrictions, which I don't have much experience with. I'm surprised that File.Copy() would cause issues, since it's just reading, but it's a consistent crash with Terraria.

逐步执行OnChanged调用已显示Terraria写入临时文件,然后将其复制到实际的保存文件中并删除临时文件.很常见.

Stepping through the OnChanged calls has shown that Terraria writes temporary files, then copies them to the actual save files, and delete the temps. Pretty common.

这是我的klutzy解决方法的代码.它使用两个计时器:_delayTimer在首次调用OnChanged时启动,而_canBackupTimer在_delayTimer过去时启动.我测试过的游戏中没有任何问题.

So here's my code with the klutzy workaround I've got. It uses two timers: _delayTimer that starts when OnChanged is first called, and _canBackupTimer when _delayTimer elapses. I haven't had any issues in the games I've tested it with.

对于每个计时器,间隔为5秒,并且AutoReset = True,因此它将在经过一次后停止.

For each Timer, the interval is 5 seconds and AutoReset = True, so it will stop after elapsing once.

这是我在监视游戏时避免IO异常的唯一方法吗?必须有一个更好的方法,但是我不确定在哪里看.我很惊讶File.Copy会限制对游戏保存过程的访问.

Is this the only way I can avoid IO Exceptions with the game being monitored? There has to be a better way, but I'm not sure where to look. I'm surprised that File.Copy would be restricting access to the game's save process.

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

Should I look at file access rights?

    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;
        }
    }

推荐答案

我的帖子考虑了有关Terraria的评论.我还没有尝试过,但是如果我想最大程度地复制文件同时又要避免Terraria崩溃的话,这就是我要解决的问题.

My post takes the comments about Terraria into account. I haven't tried this, but here's how I would approach the problem if I wanted the best chance of copying the file while keeping Terraria from crashing.

我的猜测是Terraria崩溃,因为它在您尝试复制文件时需要对文件的写访问权.我还因此假定File.Copy打开的文件没有共享访问权,因此解决该问题需要一种不同的机制来打开和复制文件.

My guess is that Terraria is crashing because it needs write access to the file while you are trying to copy it. I also thereby assume that File.Copy opens the file with no sharing access, so a different mechanism for opening and copying the file is needed to solve the problem.

您需要某种方式来打开和读取文件,同时仍然使Terraria有权同时打开和写入文件.我不是100%肯定这种方法会做到这一点,这取决于Terraria尝试打开文件的方式,但是您至少可以打开文件,以便仍然允许共享.代替File.Copy,请尝试:

You need some way to open and read from the file, while still leaving Terraria the right to simultaneously open and write to the file. I'm not 100% positive that this approach will do that, and it will depend on how Terraria is trying to open the file, but you can at least open the file so that sharing is still allowed. Instead of File.Copy, try:

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发出信号,则表示Terraria正在使用该文件,您需要稍后重试.无论如何,希望您能够摆脱Terraria的困扰.

Conclusion

If your open fails, or your copy fails (exception), or your FileSystemWatcher signals while you are copying, it means Terraria is using the file and you will need to retry later. In any case, hopefully you will be able to stay out of Terraria's way.

我非常想知道这种方法是否对您有用.如果您尝试一下,请发表评论.

I am very interested to hear if this approach works for you. Please comment if you give it a try.

这篇关于为什么使用似乎来自读取文件的File.Copy()看到IO异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆