繁体   English   中英

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

[英]Failing to programatically overwrite a file in an IIS Virtual Directory/Application (file is always locked)

起初我以为我面临一个非常简单的任务。 但是现在我意识到它不像我想像的那样有效,所以现在我希望大家能帮助我,因为目前我很困。

我的情况是这样的(在Windows 2008 R2 Server上):

  1. 每天将文件上传3次到FTP目录。 文件名总是相同的,这意味着现有文件每次都会被覆盖。
  2. 我已经编写了一个简单的C#服务,该服务正在监视FTP上传目录,为此我正在使用FileSystemWatcher类。
  3. 文件的上传需要几分钟,因此一旦File Watcher注册了更改,我就会定期尝试打开文件,以查看文件是否仍在上传(或锁定)
  4. 一旦文件不再锁定,我尝试将文件移到IIS虚拟目录中。 我必须先删除旧文件,然后再移动新文件。 这是我的问题开始的地方。 该文件似乎总是被IIS锁定(w3wp.exe进程)。

经过研究,我发现我必须终止锁定文件的进程(在这种情况下为w3wp.exe)。 为了做到这一点,我创建了一个新的应用程序池,并将虚拟目录转换为一个应用程序。 现在,我的目录在一个单独的w3wp.exe进程下运行,据称我可以安全地杀死该文件并将其移至该文件。

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

接下来我不明白的是,我可以毫无问题地通过Windows资源管理器删除目标文件。 仅我的C#应用​​程序收到“文件正在被另一个进程使用”错误。 我想知道这里有什么区别...

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

Win32:如何获取拥有互斥锁的进程/线程?

^^这里的示例代码确实可以工作,但是它为每个活动进程输出打开的句柄ID。 我只是不知道如何搜索特定的文件名,或者至少不能将句柄ID解析为文件名。 WinAPI的这些东西超出了我的头脑。

使用C#,如何找出锁定文件的进程?

^^这里的示例代码正是我所需要的,但是不幸的是我无法使它正常工作。 由于示例代码大量使用WinAPI调用,因此总是抛出“ AccessViolationException”,我无法弄清楚。

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

编辑这是我的服务器代码的一些相关部分:

帮助程序功能,用于检测文件是否被锁定:

    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的上次修改日期。 客户端每10分钟检查一次日期。 因此,该文件可能已被IIS锁定,因为有些客户端会不断检查HTTP HEAD中的文件。 但是,我不明白为什么我可以通过Windows资源管理器手动删除/重命名/移动文件而没有任何问题。 为什么这样做有效,为什么我的应用程序会收到“被另一个进程锁定”异常?

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

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

大家都说

“做一个更好的方法”

没人说如何!!!

这是如何做。 因为您提到了“我的客户应用程序”,所以这里有一个关键的机会,如果您无法控制读取文件的应用程序,您将没有机会。

每次都使用新文件名。

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

其他使这项工作有效的关键是更新频率低(文件建立得不会太糟糕),以及FTP + webserver位于同一驱动器上的事实(否则,MOVE不是原子的,客户端可能会得到一半的费用)。如果FTP驱动器不同,则解决方案是将其复制到Web服务器上的临时驱动器,然后再移动)。

但是,如果您不能更改客户端,或者必须只读取一个名称,该怎么办?

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

听起来像是骇客 ,事实并非如此。 现代无锁内存高速缓存系统做类似的事情。 锁定或损坏是不可能发生的。 直到“写入”完成,读者才能看到旧版本。

加上 ,这很简单,从脚本小子到打孔卡老手的每个人都将确切地知道您要做什么。 低技术!

您正在解决问题的症状而不是根本原因的修复。 如果您想走那条路,这里是杀死进程的代码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日志文件,查看已断开连接的客户端的状态,并将状态代码与此处的状态代码进行比较。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM