简体   繁体   English

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

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

At first I thought I'm facing a very simple task. 起初我以为我面临一个非常简单的任务。 But now I realized it doesn't work as I imagined, so now I hope you people can help me out, because I'm pretty much stuck at the moment. 但是现在我意识到它不像我想像的那样有效,所以现在我希望大家能帮助我,因为目前我很困。

My scenario is this (on a Windows 2008 R2 Server): 我的情况是这样的(在Windows 2008 R2 Server上):

  1. A file gets uploaded 3 times per day to a FTP directory. 每天将文件上传3次到FTP目录。 The filename is always the same, which means the existing file gets overwritten every time. 文件名总是相同的,这意味着现有文件每次都会被覆盖。
  2. I have programed a simple C# service which is watching the FTP upload directory, I'm using the FileSystemWatcher class for this. 我已经编写了一个简单的C#服务,该服务正在监视FTP上传目录,为此我正在使用FileSystemWatcher类。
  3. The upload of the file takes a few minutes, so once the File Watcher registers a change, I'm periodically trying to open the file, to see if the file is still being uploaded (or locked) 文件的上传需要几分钟,因此一旦File Watcher注册了更改,我就会定期尝试打开文件,以查看文件是否仍在上传(或锁定)
  4. Once the file isn't locked anymore, I try to move the file over to my IIS Virtual Directory. 一旦文件不再锁定,我尝试将文件移到IIS虚拟目录中。 I have to delete the old file first, and then move the new file over. 我必须先删除旧文件,然后再移动新文件。 This is where my problem starts. 这是我的问题开始的地方。 The file seems to be always locked by IIS (the w3wp.exe process). 该文件似乎总是被IIS锁定(w3wp.exe进程)。

After some research, I found out that I have to kill the process which is locking the file (w3wp.exe in this case). 经过研究,我发现我必须终止锁定文件的进程(在这种情况下为w3wp.exe)。 In order to do this, I have created a new application pool and converted the virtual directory into an application. 为了做到这一点,我创建了一个新的应用程序池,并将虚拟目录转换为一个应用程序。 Now my directory is running under a seperate w3wp.exe process, which I supposedly can safely kill and move the new file over there. 现在,我的目录在一个单独的w3wp.exe进程下运行,据称我可以安全地杀死该文件并将其移至该文件。

Now I just need to find the proper w3wp.exe process (there are 3 w3wp.exe processes running in total, each running under a seperate application pool) which has the lock on my target file. 现在,我只需要找到适当的w3wp.exe进程(总共有3个w3wp.exe进程在运行,每个进程在单独的应用程序池下运行)就可以锁定我的目标文件。 But this seems to be an almost impossible task in C#. 但这似乎是C#中几乎不可能完成的任务。 I found many questions here on SO regarding "Finding process which locked a specific file", but none of the answers helped me. 我在SO上发现了许多有关“锁定特定文件的查找过程”的问题,但是没有一个答案对我有帮助。 Process Explorer for example is exactly telling me which process is locking my file. 例如,Process Explorer可以准确地告诉我哪个进程正在锁定我的文件。

The next thing I don't understand is, that I can delete the target file through Windows Explorer without any problem. 接下来我不明白的是,我可以毫无问题地通过Windows资源管理器删除目标文件。 Just my C# application gets the "File is being used by another process" error. 仅我的C#应用​​程序收到“文件正在被另一个进程使用”错误。 I wonder what's the difference here... 我想知道这里有什么区别...

Here are the most notable questions on SO regarding locked files and C#: 以下是关于锁定文件和C#的最值得注意的问题:

Win32: How to get the process/thread that owns a mutex? Win32:如何获取拥有互斥锁的进程/线程?

^^ The example code here does actually work, but this outputs the open handle IDs for every active process. ^^这里的示例代码确实可以工作,但是它为每个活动进程输出打开的句柄ID。 I just can't figure out how to search for a specific filename, or at least resolve the handle ID to a filename. 我只是不知道如何搜索特定的文件名,或者至少不能将句柄ID解析为文件名。 This WinAPI stuff is way above my head. WinAPI的这些东西超出了我的头脑。

Using C#, how does one figure out what process locked a file? 使用C#,如何找出锁定文件的进程?

^^ The example code here is exactly what I need, but unfortunately I can't get it to work. ^^这里的示例代码正是我所需要的,但是不幸的是我无法使它正常工作。 It is always throwing an "AccessViolationException" which I can't figure out, since the sample code is making extensive use of WinAPI calls. 由于示例代码大量使用WinAPI调用,因此总是抛出“ AccessViolationException”,我无法弄清楚。

Simple task, impossible to do? 简单的任务,不可能做? I appreciate any help. 感谢您的帮助。

EDIT Here are some relevant parts of my server code: 编辑这是我的服务器代码的一些相关部分:

Helper function to detect if a file is locked: 帮助程序功能,用于检测文件是否被锁定:

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

And this is the code in my FileSystemWatcher event, which is monitoring the FTP upload directory. 这是我的FileSystemWatcher事件中的代码,该事件正在监视FTP上传目录。 filepath is the newly uploaded file, targetfilepath is the target file in my IIS directory. 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: Killing the w3wp.exe process while clients are downloading the file would be no problem for us. EDIT2:在客户端下载文件时杀死w3wp.exe进程对我们来说不是问题。 I'm just having a hard time finding the right w3wp.exe process which is locking the file. 我很难找到合适的w3wp.exe进程来锁定文件。

Also, my client application, which is downloading the file on the clients, is checking the HTTP HEAD for the Last-Modified date. 另外,我的客户端应用程序正在客户端上下载文件,正在检查HTTP HEAD的上次修改日期。 The client is checking the date every 10 minutes. 客户端每10分钟检查一次日期。 So it is possible that the file is being locked by IIS because there are clients continously checking the HTTP HEAD for the file. 因此,该文件可能已被IIS锁定,因为有些客户端会不断检查HTTP HEAD中的文件。 Nonetheless, I don't understand why I can manually delete/rename/move the file through windows explorer without any problems. 但是,我不明白为什么我可以通过Windows资源管理器手动删除/重命名/移动文件而没有任何问题。 Why does this work, and why does my application get a "Locked by another process" exception? 为什么这样做有效,为什么我的应用程序会收到“被另一个进程锁定”异常?

One problem I've run into is that a file exists while it is still being written, which means it would be locked as well. 我遇到的一个问题是文件仍在写入时存在,这意味着它也将被锁定。 If your FileReadable() function were called at this time, it would return false. 如果此时调用了FileReadable()函数,它将返回false。

My solution was to, in the proc which writes the file, write the file to, say, OUTPUT1.TXT, and then after it is fully written and the FileStream closed, rename it to OUTPUT2.TXT. 我的解决方案是,在写入文件的proc中,将文件写入例如OUTPUT1.TXT,然后在完全写入文件并关闭FileStream之后,将其重命名为OUTPUT2.TXT。 This way, the existence of OUTPUT2.TXT indicates that the file is written and (hopefully) unlocked. 这样,OUTPUT2.TXT的存在指示文件已被写入和(希望)已解锁。 Simply check for OUTPUT2.TXT in your FileReadable() loop. 只需在FileReadable()循环中检查OUTPUT2.TXT。

Everybody say... 大家都说

"Do it a better way" “做一个更好的方法”

Nobody say how!!! 没人说如何!!!

Here's how. 这是如何做。 Because you mentioned 'My Client Application,' there is a key opportunity here that you would not have if you didn't have control over the apps reading the file. 因为您提到了“我的客户应用程序”,所以这里有一个关键的机会,如果您无法控制读取文件的应用程序,您将没有机会。

Just use new filenames each time. 每次都使用新文件名。

You have control of the program reading and writing the files. 您可以控制程序读取和写入文件。 Put an incrementing # in the filesnames, have the client pick the biggest # (Actually the latest date, then your numbers can wrap around). 在文件名中添加一个递增的#号,让客户选择最大的#号(实际上是最新日期,然后您的数字即可环绕)。 Have the writer program clean up old files if it can ; 如果可以的作家程序清理旧文件; if not, they won't hurt anything. 如果没有,他们不会伤害任何东西。 IIS will eventually let go of them. IIS最终将放弃它们。 If not, just open up explorer every week and do it yourself! 如果没有,那就每周打开一个资源管理器,然后自己动手!

Other keys that make this work are the low frequency of updates (files won't build up too bad), and the fact that the FTP+webserver are on the same drive (Otherwise the MOVE is not atomic and clients could get a half-copied file. Solution if FTP drive is different would be to copy to a temp drive on the webserver then move). 其他使这项工作有效的关键是更新频率低(文件建立得不会太糟糕),以及FTP + webserver位于同一驱动器上的事实(否则,MOVE不是原子的,客户端可能会得到一半的费用)。如果FTP驱动器不同,则解决方案是将其复制到Web服务器上的临时驱动器,然后再移动)。

but what if you can't change the client or it has to read just one name? 但是,如果您不能更改客户端,或者必须只读取一个名称,该怎么办?

Front-end it with a script. 在前端使用脚本。 Have the client hit an ASPX that sets the right HTTP headers and has the 'pick the right file' logic, and spits out the file contents. 让客户端点击设置正确的HTTP标头并具有“选择正确的文件”逻辑的ASPX,然后吐出文件内容。 This is a very popular trick pages use to write images stored on a database out to the browser, while the img tag appears to read from a file. 这是一个非常流行的技巧页面,用于将存储在数据库中的图像写到浏览器中,而img标签似乎是从文件中读取的。 (google along that lines for sample code). (Google沿这行提供示例代码)。

sounds like a hack , it's not. 听起来像是骇客 ,事实并非如此。 Modern lockless memory cache systems do a similar thing. 现代无锁内存高速缓存系统做类似的事情。 It is impossible for a lock or corruption to occur; 锁定或损坏是不可能发生的。 until the 'write' is complete, readers see the old version. 直到“写入”完成,读者才能看到旧版本。

plus , it's simple, everybody from a script kiddie to a punchcard vetern will know exactly what you're up to. 加上 ,这很简单,从脚本小子到打孔卡老手的每个人都将确切地知道您要做什么。 Go low-tech! 低技术!

You're troubleshooting a symptom of the problem not a fix for the root cause. 您正在解决问题的症状而不是根本原因的修复。 If you want to go down that path here is the code to kill processes http://www.codeproject.com/Articles/20284/My-TaskManager - but the better idea would be to do it properly and work out whats wrong. 如果您想走那条路,这里是杀死进程的代码http://www.codeproject.com/Articles/20284/My-TaskManager-但更好的主意是正确执行此操作并找出问题所在。 I suggest in the Catch Exception of FileReadable: 我建议在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. Turn off any Anti-Virus software and re-test 关闭所有防病毒软件,然后重新测试
  2. Increase the polling timeout duration to see if its just a timing thing 增加轮询超时时间以查看其是否只是计时
  3. Check the FTP logfile and see the status for the disconnected client and compare the status code with the ones here . 检查FTP日志文件,查看已断开连接的客户端的状态,并将状态代码与此处的状态代码进行比较。

I don't see in your sample code where you are closing your file stream. 我在您的示例代码中看不到您要关闭文件流的位置。 Keeping the file stream open will keep a lock on the file. 保持文件流打开将锁定文件。 It would be a good idea to close the stream. 关闭流是一个好主意。 You probably don't want to be killing your w3wp.exe process, as others here have mentioned. 您可能不想杀死w3wp.exe进程,就像这里其他人提到的那样。

restarting IIS can unlock the file taken by w3wp.exe. 重新启动IIS可以解锁w3wp.exe占用的文件。

cmd (run as administrator) -> iisreset /stop -> update/delete file in windows explorer -> iisreset /start cmd(以管理员身份运行)-> iisreset / stop->更新/删除Windows资源管理器中的文件-> iisreset / start

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

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