无法以编程方式覆盖IIS虚拟目录/应用程序中的文件(文件始终处于锁定状态)

时间:2012-05-07 09:51:40

标签: c# iis file-locking

起初我以为我面临着一项非常简单的任务。但是现在我意识到它并没有像我想象的那样起作用,所以现在我希望你们有人可以帮助我,因为我现在几乎陷入困境。

我的方案是这样的(在Windows 2008 R2服务器上):

  1. 文件每天上传3次到FTP目录。文件名始终相同,这意味着每次都会覆盖现有文件。
  2. 我编写了一个简单的C#服务,它正在观看FTP上传目录,我正在使用FileSystemWatcher类。
  3. 上传文件需要几分钟,因此一旦文件观察程序注册更改,我会定期尝试打开文件,以查看文件是否仍在上传(或锁定)
  4. 一旦文件不再被锁定,我会尝试将文件移动到我的IIS虚拟目录。我必须先删除旧文件,然后移动新文件。这是我的问题开始的地方。该文件似乎总是被IIS锁定(w3wp.exe进程)。
  5. 经过一番研究,我发现我必须杀死锁定文件的进程(在这种情况下是w3wp.exe)。为此,我创建了一个新的应用程序池,并将虚拟目录转换为一个应用程序。现在我的目录在一个单独的w3wp.exe进程下运行,据说我可以安全地杀死并在那里移动新文件。

    现在我只需要找到正确的w3wp.exe进程(总共运行3个w3wp.exe进程,每个进程在一个单独的应用程序池下运行),该进程锁定了我的目标文件。但这似乎是C#中几乎不可能完成的任务。我在这里发现了许多关于“查找锁定特定文件的过程”的问题,但没有一个答案对我有帮助。 例如,Process Explorer正好告诉我哪个进程正在锁定我的文件。

    接下来我不明白的是,我可以通过Windows资源管理器删除目标文件而没有任何问题。只是我的C#应用​​程序获取“文件被另一个进程使用”错误。我想知道这里有什么不同......

    以下是有关锁定文件和C#的最值得注意的问题:

    Win32: How to get the process/thread that owns a mutex?

    ^^ 这里的示例代码确实有效,但是这会为每个活动进程输出打开的句柄ID。我只是无法弄清楚如何搜索特定的文件名,或至少解析文件名的句柄ID。这个WinAPI的东西远远超出我的想象。

    Using C#, how does one figure out what process locked a file?

    ^^ 这里的示例代码正是我所需要的,但不幸的是我无法让它工作。它始终抛出一个我无法弄清楚的“AccessViolationException”,因为示例代码正在大量使用WinAPI调用。

    简单的任务,不可能做到的?我感谢任何帮助。

    修改 以下是我的服务器代码的一些相关部分:

    帮助函数检测文件是否被锁定:

        private bool FileReadable(string file, int timeOutSeconds)
        {
            DateTime timeOut = DateTime.Now.AddSeconds(timeOutSeconds);
    
            while (DateTime.Now < timeOut)
            {
                try
                {
                    if (File.Exists(file))
                    {
                        using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None))
                        {
                            return true;
                        }
                    }
                    return false;
                }
                catch (Exception)
                {
                    Thread.Sleep(500);
                }
            }
    
            m_log.LogLogic(0, "FileReadable", "Timeout after [{0}] seconds trying to open the file {1}", timeOutSeconds, file);
            return false;
        }
    

    这是我的FileSystemWatcher事件中的代码,它监视FTP上载目录。 filepath是新上传的文件,targetfilepath是我的IIS目录中的目标文件。

            // here I'm waiting for the newly uploaded file to be ready
            if (FileReadable(filepath, FWConfig.TimeOut))
            {
                // move uploaded file to IIS virtual directory
                string targetfilepath = Path.Combine(FWConfig.TargetPath, FWConfig.TargetFileName);
    
                if(File.Exists(targetfilepath))
                {
                    m_log.LogLogic(4, "ProcessFile", "Trying to delete old file first: [{0}]", targetfilepath);
                    // targetfilepath is the full path to my file in my IIS directory
                    // always fails because file is always locked my w3wp.exe :-(
                    if(FileReadable(targetfilepath, FWConfig.TimeOut))
                        File.Delete(targetfilepath);
                }
    
                File.Move(filepath, targetfilepath);
            }
    

    EDIT2: 在客户端下载文件时杀死w3wp.exe进程对我们来说没有问题。我只是很难找到正确锁定文件的w3wp.exe进程。

    此外,我在客户端上下载文件的客户端应用程序正在检查HTTP HEAD的Last-Modified日期。客户每10分钟检查一次日期。因此,IIS可能会锁定该文件,因为有些客户端不断检查文件的HTTP HEAD。尽管如此,我不明白为什么我可以通过Windows资源管理器手动删除/重命名/移动文件没有任何问题。为什么这样做,以及为什么我的应用程序会被“另一个进程锁定”异常?

5 个答案:

答案 0 :(得分:3)

我遇到的一个问题是文件仍在被写入时存在,这意味着它也会被锁定。如果此时调用了FileReadable()函数,它将返回false。

我的解决办法是,在写入文件的proc中,将文件写入,例如OUTPUT1.TXT,然后在完全写入并关闭FileStream之后,将其重命名为OUTPUT2.TXT。这样,OUTPUT2.TXT的存在表明文件已被写入并且(希望)已解锁。只需在FileReadable()循环中检查OUTPUT2.TXT即可。

答案 1 :(得分:3)

每个人都说......

“做得更好”

没人说怎么做!!!

这是怎么回事。因为您提到了“我的客户端应用程序”,所以如果您无法控制阅读该文件的应用程序,那么您将无法拥有这个机会。

每次只使用新的文件名。

您可以控制程序读取和写入文件。在文件名中加上一个递增的#,让客户端选择最大的#(实际上是最新的日期,然后你的数字可以包围)。让编写器程序清理旧文件如果可以;如果没有,他们不会伤害任何东西。 IIS最终将放弃它们。如果没有,请每周打开资源管理器并自己动手!

使这项工作的其他关键是更新频率低(文件不会太差),以及FTP +网络服务器在同一驱动器上的事实(否则MOVE不是原子的,客户端可能会得到一个半复制的文件。如果FTP驱动器不同,解决方法是复制到网络服务器上的临时驱动器,然后移动)。

但如果您无法更改客户端或只需读取一个名称该怎么办?

使用脚本前端。让客户端点击一个ASPX,设置正确的HTTP头并具有“选择正确的文件”逻辑,并吐出文件内容。这是一个非常流行的技巧页面,用于将存储在数据库中的图像写入浏览器,而img标签似乎从文件中读取。 (google沿着这一行获取示例代码)。

听起来像是黑客,但事实并非如此。现代无锁内存缓存系统也做类似的事情。锁定或腐败是不可能发生的;直到'写'完成,读者才能看到旧版本。

plus ,这很简单,从剧本小家伙到穿孔卡片的每个人都会知道你到底要做什么。低技术!

答案 2 :(得分:0)

您正在排查问题的症状,而不是修复根本原因。如果你想沿着这条路走下去就是杀死进程的代码http://www.codeproject.com/Articles/20284/My-TaskManager - 但更好的想法是正确地做到这一点并找出错误的方法。我建议在FileReadable的Catch Exception中使用:

catch (Exception ex) {
if (ex is IOException && IsFileLocked(ex)) {
//Confirm the code see's it as a FileLocked issue, not some other exception
//its not safe to unlock files used by other processes, because the other process is likely reading/writing it. 
}
}

private static bool IsFileLocked(Exception exception)
{
    int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
    return errorCode == 32 || errorCode == 33;
}
  1. 关闭所有防病毒软件并重新测试
  2. 增加轮询超时持续时间以查看它是否只是一个计时事项
  3. 检查FTP日志文件并查看断开连接的客户端的状态,并将状态代码与here进行比较。

答案 3 :(得分:0)

我在示例代码中没有看到您关闭文件流的位置。保持文件流打开将锁定文件。关闭流是个好主意。你可能不想像你在这里提到的那样杀死你的w3wp.exe进程。

答案 4 :(得分:0)

重新启动IIS可以解锁w3wp.exe所占用的文件。

  

cmd(以管理员身份运行) - &gt; iisreset / stop - &gt;更新/删除文件   Windows资源管理器 - &gt; iisreset / start