82

あるディレクトリにファイルが作成されると(FileSystemWatcher_Created)、別のディレクトリにコピーします。しかし、大きな(> 10MB)ファイルを作成すると、ファイルのコピーが失敗します。これは、ファイルがまだ作成を完了していないときに、すでにコピーを開始しているためです...
これにより、別のプロセスで使用されているため、ファイルをコピーできません。上げた。;(
何か助けはありますか?

class Program
{
    static void Main(string[] args)
    {
        string path = @"D:\levan\FolderListenerTest\ListenedFolder";
        FileSystemWatcher listener; 
        listener = new FileSystemWatcher(path);
        listener.Created += new FileSystemEventHandler(listener_Created);
        listener.EnableRaisingEvents = true;

        while (Console.ReadLine() != "exit") ;
    }

    public static void listener_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine
                (
                    "File Created:\n"
                   + "ChangeType: " + e.ChangeType
                   + "\nName: " + e.Name
                   + "\nFullPath: " + e.FullPath
                );
        File.Copy(e.FullPath, @"D:\levan\FolderListenerTest\CopiedFilesFolder\" + e.Name);
        Console.Read();
    }
}
4

9 に答える 9

48

直面している問題の回避策のみがあります。

コピーの処理を開始する前に、ファイルIDが処理中であるかどうかを確認してください。False値を取得するまで、次の関数を呼び出すことができます。

この回答から直接コピーされた最初の方法:

private bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

2番目の方法:

const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;
private bool IsFileLocked(string file)
{
    //check that problem is not in destination file
    if (File.Exists(file) == true)
    {
        FileStream stream = null;
        try
        {
            stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        }
        catch (Exception ex2)
        {
            //_log.WriteLog(ex2, "Error in checking whether file is locked " + file);
            int errorCode = Marshal.GetHRForException(ex2) & ((1 << 16) - 1);
            if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION))
            {
                return true;
            }
        }
        finally
        {
            if (stream != null)
                stream.Close();
        }
    }
    return false;
}
于 2012-06-11T14:37:11.410 に答える
17

のドキュメントからFileSystemWatcher

OnCreatedファイルが作成されるとすぐにイベントが発生します。ファイルが監視対象ディレクトリにコピーまたは転送されている場合、 OnCreatedイベントはすぐに発生し、その後に1つ以上の OnChangedイベントが発生します。

したがって、コピーが失敗した場合(例外をキャッチ)、それをまだ移動する必要があるファイルのリストに追加し、OnChangedイベント中にコピーを試行します。最終的には、機能するはずです。

(不完全、特定の例外をキャッチ、変数を初期化するなど)のようなもの:

public static void listener_Created(object sender, FileSystemEventArgs e)
{
    Console.WriteLine
            (
                "File Created:\n"
               + "ChangeType: " + e.ChangeType
               + "\nName: " + e.Name
               + "\nFullPath: " + e.FullPath
            );
    try {
        File.Copy(e.FullPath, @"D:\levani\FolderListenerTest\CopiedFilesFolder\" + e.Name);
    }
    catch {
        _waitingForClose.Add(e.FullPath);
    }
    Console.Read();
}

public static void listener_Changed(object sender, FileSystemEventArgs e)
{
     if (_waitingForClose.Contains(e.FullPath))
     {
          try {
              File.Copy(...);
              _waitingForClose.Remove(e.FullPath);
          }
          catch {}
     }

}

于 2012-06-11T14:36:26.333 に答える
11

古いスレッドですが、他の人のためにいくつかの情報を追加します。

PDFファイルを書き込むプログラムで同様の問題が発生しました。レンダリングに30秒かかる場合があります。これは、watcher_FileCreatedクラスがファイルをコピーする前に待機する時間と同じです。

ファイルはロックされていません。

この場合、PDFのサイズを確認し、2秒待ってから新しいサイズを比較しました。サイズが等しくない場合、スレッドは30秒間スリープし、再試行します。

于 2015-05-20T02:24:56.323 に答える
5

あなたは実際に運がいいです-ファイルを書いているプログラムはそれをロックするので、あなたはそれを開くことができません。ロックされていなかったとしたら、問題があるとは思わずに、ファイルの一部をコピーしていたでしょう。

ファイルにアクセスできない場合は、ファイルがまだ使用中であると見なすことができます(File.Copyの失敗から推測するのではなく、排他モードで開いて、他の誰かが現在開いているかどうかを確認してください)。ファイルがロックされている場合は、別のときにコピーする必要があります。ロックされていない場合は、コピーできます(ここで競合状態が発生する可能性がわずかにあります)。

その「別の時間」はいつですか?FileSystemWatcherがファイルごとに複数のイベントを送信する場合は覚えていません。チェックしてください。イベントを無視して別のイベントを待つだけで十分な場合があります。そうでない場合は、いつでも時間を設定し、5秒でファイルを再確認できます。

于 2012-06-11T14:35:20.043 に答える
3

さて、あなたはすでに自分で答えを出します。ファイルの作成が完了するまで待つ必要があります。これを行う1つの方法は、ファイルがまだ使用されているかどうかを確認することです。この例はここにあります:ファイルが使用されているかどうかを確認する方法はありますか?

このコードを自分の状況で機能させるには、このコードを変更する必要があることに注意してください。あなたは(擬似コード)のようなものが欲しいかもしれません:

public static void listener_Created()
{
   while CheckFileInUse()
      wait 1000 milliseconds

   CopyFile()
}

while明らかに、所有者アプリケーションがロックを解放しない場合に備えて、無限から身を守る必要があります。FileSystemWatcherまた、購読できる他のイベントをチェックする価値があるかもしれません。この問題全体を回避するために使用できるイベントがあるかもしれません。

于 2012-06-11T14:37:33.317 に答える
2

ファイルがバイナリ(バイトごと)で書き込まれている場合は、FileStream以上のソリューションを作成します。ファイルは準備ができており、すべてのバイトで書き込まれるため、機能しません。この状況では、次のような他の回避策が必要です。ファイルが作成されたとき、または必要なときにこれを行うファイルの処理を開始します

long fileSize = 0;
currentFile = new FileInfo(path);

while (fileSize < currentFile.Length)//check size is stable or increased
{
  fileSize = currentFile.Length;//get current size
  System.Threading.Thread.Sleep(500);//wait a moment for processing copy
  currentFile.Refresh();//refresh length value
}

//Now file is ready for any process!
于 2016-11-15T13:24:43.943 に答える
1

そこで、これらの質問や他の同様の質問のいくつかをすばやく確認した後、同期(およびファイル保存)方法としてファイルを使用する2つの別々のプログラムの問題を解決するために、今日の午後に陽気な追跡を行いました。少し変わった状況ですが、「ファイルがロックされているかどうかを確認し、ロックされていない場合は開く」というアプローチの問題がはっきりと浮き彫りになりました。

問題はこれです:ファイルをチェックしてから実際にファイルを開くまでの間にファイルがロックされる可能性があります。散発的なファイルをコピーできません。ファイルを探していない場合は別のプロセスエラーによって使用されるため、追跡するのは非常に困難です。

基本的な解決策は、catchブロック内でファイルを開こうとすることです。これにより、ファイルがロックされている場合は、再試行できます。そうすれば、チェックからオープンまでの経過時間はなく、OSは同時にそれらを実行します。

ここでのコードはFile.Copyを使用していますが、Fileクラスの静的メソッド(File.Open、File.ReadAllText、File.WriteAllTextなど)でも同様に機能します。

/// <param name="timeout">how long to keep trying in milliseconds</param>
static void safeCopy(string src, string dst, int timeout)
{
    while (timeout > 0)
    {
        try
        {
            File.Copy(src, dst);

            //don't forget to either return from the function or break out fo the while loop
            break;
        }
        catch (IOException)
        {
            //you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible
        }
        Thread.Sleep(100);

        //if its a very long wait this will acumulate very small errors. 
        //For most things it's probably fine, but if you need precision over a long time span, consider
        //   using some sort of timer or DateTime.Now as a better alternative
        timeout -= 100;
    }
}

並列処理に関するもう1つの小さな注意: これは同期メソッドであり、待機中とスレッドでの作業中の両方でスレッドをブロックします。これは最も簡単な方法ですが、ファイルが長時間ロックされたままになると、プログラムが応答しなくなる可能性があります。並列処理はあまりにも大きなトピックであり、ここで詳しく説明することはできません(非同期の読み取り/書き込みを設定できる方法の数は、やや馬鹿げています)が、並列化できる1つの方法があります。

public class FileEx
{
    public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone)
    {
        while (timeout > 0)
        {
            try
            {
                File.Copy(src, dst);
                doWhenDone();
                break;
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
    }

    public static async Task<string> ReadAllTextWaitAsync(string filePath, int timeout)
    {
        while (timeout > 0)
        {
            try {
                return File.ReadAllText(filePath);
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
        return "";
    }

    public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout)
    {
        while (timeout > 0)
        {
            try
            {
                File.WriteAllText(filePath, contents);
                return;
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
    }
}

そして、これがどのように使用できるかです:

public static void Main()
{
    test_FileEx();
    Console.WriteLine("Me First!");
}    

public static async void test_FileEx()
{
    await Task.Delay(1);

    //you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy
    //As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously
    //until the first 'await'. See the documentation for async: https://msdn.microsoft.com/en-us/library/hh156513.aspx
    CopyWaitAsync("file1.txt", "file1.bat", 1000);

    //this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes
    await CopyWaitAsync("file1.txt", "file1.readme", 1000);
    Console.WriteLine("file1.txt copied to file1.readme");

    //The following line doesn't cause a compiler error, but it doesn't make any sense either.
    ReadAllTextWaitAsync("file1.readme", 1000);

    //To get the return value of the function, you have to use this function with the await keyword
    string text = await ReadAllTextWaitAsync("file1.readme", 1000);
    Console.WriteLine("file1.readme says: " + text);
}

//Output:
//Me First!
//file1.txt copied to file1.readme
//file1.readme says: Text to be duplicated!
于 2017-01-06T02:26:21.103 に答える
0

次のコードを使用して、ファイルを排他的アクセスで開くことができるかどうか(つまり、別のアプリケーションで開かれていないかどうか)を確認できます。ファイルが閉じられていない場合は、しばらく待ってから、ファイルが閉じられて安全にコピーできるようになるまでもう一度確認してください。

File.Copyが失敗するかどうかを確認する必要があります。これは、ファイルを確認してからコピーするまでの間に、別のアプリケーションがファイルを開く可能性があるためです。

public static bool IsFileClosed(string filename)
{
    try
    {
        using (var inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            return true;
        }
    }
    catch (IOException)
    {
        return false;
    }
}
于 2012-06-11T14:43:10.473 に答える
-5

これが私のために働いたので、私はここに答えを追加したいと思います。私は時間遅延、whileループ、考えられるすべてのものを使用しました。

出力フォルダーのWindowsエクスプローラーウィンドウを開きました。私はそれを閉じました、そしてすべてが魅力のように働きました。

これが誰かに役立つことを願っています。

于 2015-07-30T12:47:02.167 に答える