2

最初は、非常に単純なタスクに直面していると思いました。しかし、想像していたようにうまくいかないことに気付きました。今、私はほとんど立ち往生しているので、皆さんが私を助けてくれることを願っています.

私のシナリオはこれです(Windows 2008 R2サーバー上):

  1. ファイルは 1 日に 3 回、FTP ディレクトリにアップロードされます。ファイル名は常に同じです。つまり、既存のファイルは毎回上書きされます。
  2. FTP アップロード ディレクトリを監視する単純な C# サービスをプログラムしました。これには 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# に関する SO に関する最も注目すべき質問は次のとおりです。

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

これは、FTP アップロード ディレクトリを監視している FileSystemWatcher イベントのコードです。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 分ごとに日付をチェックしています。そのため、ファイルの HTTP HEAD を継続的にチェックしているクライアントが存在するため、IIS によってファイルがロックされている可能性があります。それにもかかわらず、Windowsエクスプローラーを介してファイルを問題なく手動で削除/名前変更/移動できる理由がわかりません。これが機能するのはなぜですか? また、アプリケーションで「別のプロセスによってロックされています」という例外が発生するのはなぜですか?

4

5 に答える 5

3

私が遭遇した問題の 1 つは、ファイルがまだ書き込まれている間にファイルが存在することです。つまり、ファイルもロックされます。この時点で FileReadable() 関数が呼び出された場合、false が返されます。

私の解決策は、ファイルを書き込むプロシージャで、たとえば OUTPUT1.TXT にファイルを書き込み、ファイルが完全に書き込まれて FileStream が閉じられたら、ファイルの名前を OUTPUT2.TXT に変更することでした。このように、OUTPUT2.TXT の存在は、ファイルが書き込まれ、(うまくいけば) ロックされていないことを示します。FileReadable() ループで OUTPUT2.TXT を確認するだけです。

于 2012-05-10T15:52:43.970 に答える
3

誰もが言う...

「もっといい方法で」

誰も方法を言いません!!!

方法は次のとおりです。「マイ クライアント アプリケーション」について言及されたので、ファイルを読み取るアプリを制御できなければ得られなかった重要な機会がここにあります。

毎回新しいファイル名を使用してください。

ファイルの読み取りと書き込みを行うプログラムを制御できます。ファイル名にインクリメントする # を入れ、クライアントに最大の # を選択してもらいます (実際には最新の日付で、番号は循環できます)。可能であれば、書き込みプログラムに古いファイルをクリーンアップさせます。そうでなければ、彼らは何も傷つけません。IIS は最終的にそれらを手放します。そうでない場合は、毎週エクスプローラーを開いて自分でやってください!

これを機能させるその他の鍵は、更新の頻度が低いこと (ファイルがひどく蓄積されないこと) と、FTP+Web サーバーが同じドライブ上にあるという事実です (そうしないと、MOVE はアトミックではなく、クライアントは半分のエラーを取得する可能性があります)。 FTP ドライブが異なる場合の解決策は、Web サーバーの一時ドライブにコピーしてから移動することです)。

しかし、クライアントを変更できない場合、または 1 つの名前だけを読み取らなければならない場合はどうでしょうか。

スクリプトでフロントエンドします。クライアントに、適切な HTTP ヘッダーを設定し、「適切なファイルを選択する」ロジックを持つ ASPX をヒットさせ、ファイルの内容を吐き出させます。これは、img タグがファイルから読み取られているように見える一方で、データベースに保存されている画像をブラウザーに書き込むために使用される非常に一般的なトリック ページです。(サンプルコードについては、その行に沿ってグーグルで検索してください)。

ハックのように聞こえますが、そうではありません。最新のロックレス メモリ キャッシュ システムは、同様のことを行います。ロックや破損が発生することはありません。「書き込み」が完了するまで、読者には古いバージョンが表示されます。

さらに、簡単です。スクリプトのキディからパンチカードのベテランまで、誰もがあなたが何をしているのかを正確に知っています。ローテクに行こう!

于 2012-05-14T20:37:42.330 に答える
0

サンプルコードには、ファイルストリームを閉じている場所が表示されません。ファイルストリームを開いたままにしておくと、ファイルがロックされたままになります。ストリームを閉じることをお勧めします。ここで他の人が言及しているように、おそらくw3wp.exeプロセスを強制終了したくないでしょう。

于 2012-05-14T15:17:11.187 に答える
0

IIS を再起動すると、w3wp.exe が取得したファイルのロックを解除できます。

cmd (管理者として実行) -> iisreset /stop -> Windows エクスプローラーでファイルを更新/削除 -> iisreset /start

于 2013-08-28T11:21:17.893 に答える
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 ログファイルをチェックして、切断されたクライアントのステータスを確認し、ステータス コードをここにあるものと比較します。
于 2012-05-12T03:39:18.747 に答える