C# 等到文件完全写入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10982104/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 16:00:41  来源:igfitidea点击:

Wait Until File Is Completely Written

c#fileiofilesystemscopy

提问by levi

When a file is created (FileSystemWatcher_Created) in one directory I copy it to another. But When I create a big (>10MB) file it fails to copy the file, because it starts copying already, when the file is not yet finished creating...
This causes Cannot copy the file, because it's used by another processto be raised. ;(
Any help?

在一个目录中创建文件 ( 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();
    }
}

采纳答案by Romil Kumar Jain

There is only workaround for the issue you are facing.

您面临的问题只有解决方法。

Check whether file id in process before starting the process of copy. You can call the following function until you get the False value.

在开始复制过程之前检查文件 id 是否正在处理。您可以调用以下函数,直到获得 False 值。

1st Method, copied directly from this answer:

第一种方法,直接从这个答案复制:

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

2nd Method:

方法二:

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

回答by zmbq

You're actually in luck - the program writing the file locks it, so you can't open it. If it hadn't locked it, you would have copied a partial file, without having any idea there's a problem.

您实际上很幸运 - 写入文件的程序将其锁定,因此您无法打开它。如果它没有锁定它,您就会复制部分文件,而不会知道有问题。

When you can't access a file, you can assume it's still in use (better yet - try to open it in exclusive mode, and see if someone else is currently opening it, instead of guessing from the failure of File.Copy). If the file is locked, you'll have to copy it at some other time. If it's not locked, you can copy it (there's slight potential for a race condition here).

当您无法访问某个文件时,您可以假设它仍在使用中(更好 - 尝试以独占模式打开它,并查看其他人当前是否正在打开它,而不是从 File.Copy 的失败中猜测)。如果文件被锁定,您必须在其他时间复制它。如果它没有被锁定,你可以复制它(这里有轻微的竞争条件)。

When is that 'other time'? I don't rememeber when FileSystemWatcher sends multiple events per file - check it out, it might be enough for you to simply ignore the event and wait for another one. If not, you can always set up a time and recheck the file in 5 seconds.

那“其他时间”是什么时候?我不记得 FileSystemWatcher 何时为每个文件发送多个事件 - 检查一下,您只需忽略该事件并等待另一个事件就足够了。如果没有,您可以随时设置时间并在 5 秒内重新检查文件。

回答by Steve Czetty

From the documentation for FileSystemWatcher:

从文档中FileSystemWatcher

The OnCreatedevent is raised as soon as a file is created. If a file is being copied or transferred into a watched directory, the OnCreatedevent will be raised immediately, followed by one or more OnChangedevents.

OnCreated创建文件后立即引发该事件。如果一个文件正在被复制或传输到一个被监视的目录中,该 OnCreated事件将立即引发,然后是一个或多个 OnChanged事件。

So, if the copy fails, (catch the exception), add it to a list of files that still need to be moved, and attempt the copy during the OnChangedevent. Eventually, it should work.

因此,如果复制失败(捕获异常),请将其添加到仍需要移动的文件列表中,并在OnChanged事件期间尝试复制。最终,它应该工作。

Something like (incomplete; catch specific exceptions, initialize variables, etc):

类似(不完整;捕获特定异常,初始化变量等):

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

回答by pyrocumulus

Well you already give the answer yourself; you have to wait for the creation of the file to finish. One way to do this is via checking if the file is still in use. An example of this can be found here: Is there a way to check if a file is in use?

你自己已经给出了答案;您必须等待文件创建完成。一种方法是通过检查文件是否仍在使用中。一个例子可以在这里找到:有没有办法检查文件是否在使用中?

Note that you will have to modify this code for it to work in your situation. You might want to have something like (pseudocode):

请注意,您必须修改此代码才能使其适用于您的情况。你可能想要像(伪代码)这样​​的东西:

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

   CopyFile()
}

Obviously you should protect yourself from an infinite whilejust in case the owner application never releases the lock. Also, it might be worth checking out the other events from FileSystemWatcheryou can subscribe to. There might be an event which you can use to circumvent this whole problem.

显然,您应该保护自己免受无限的影响while,以防所有者应用程序永远不会释放锁。此外,可能值得查看FileSystemWatcher您可以订阅的其他事件。可能有一个事件可以用来规避整个问题。

回答by Michael

You can use the following code to check if the file can be opened with exclusive access (that is, it is not opened by another application). If the file isn't closed, you could wait a few moments and check again until the file is closed and you can safely copy it.

您可以使用以下代码来检查文件是否可以以独占访问方式打开(即不能被其他应用程序打开)。如果文件未关闭,您可以稍等片刻并再次检查,直到文件关闭并且您可以安全地复制它。

You should still check if File.Copy fails, because another application may open the file between the moment you check the file and the moment you copy it.

您仍应检查 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;
    }
}

回答by Johnny Grimes

It's an old thread, but I'll add some info for other people.

这是一个旧线程,但我会为其他人添加一些信息。

I experienced a similar issue with a program that writes PDF files, sometimes they take 30 seconds to render.. which is the same period that my watcher_FileCreated class waits before copying the file.

我在编写 PDF 文件的程序中遇到了类似的问题,有时它们需要 30 秒来呈现......这与我的 watcher_FileCreated 类在复制文件之前等待的时间相同。

The files were not locked.

文件没有被锁定。

In this case I checked the size of the PDF and then waited 2 seconds before comparing the new size, if they were unequal the thread would sleep for 30 seconds and try again.

在这种情况下,我检查了 PDF 的大小,然后在比较新大小之前等待 2 秒,如果它们不相等,线程将休眠 30 秒并重试。

回答by Firkamon

I would like to add an answer here, because this worked for me. I used time delays, while loops, everything I could think of.

我想在这里添加一个答案,因为这对我有用。我使用了时间延迟,while 循环,我能想到的一切。

I had the Windows Explorer window of the output folder open. I closed it, and everything worked like a charm.

我打开了输出文件夹的 Windows 资源管理器窗口。我关闭了它,一切都像魅力一样。

I hope this helps someone.

我希望这可以帮助别人。

回答by Mohsen.Sharify

When the file is writing in binary(byte by byte),create FileStream and above solutions Not working,because file is ready and wrotted in every bytes,so in this Situation you need other workaround like this: Do this when file created or you want to start processing on file

当文件以二进制(逐字节)写入时,创建 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!

回答by ashbygeek

So, having glanced quickly through some of these and other similar questions I went on a merry goose chase this afternoon trying to solve a problem with two separate programs using a file as a synchronization (and also file save) method. A bit of an unusual situation, but it definitely highlighted for me the problems with the 'check if the file is locked, then open it if it's not' approach.

因此,在快速浏览了其中的一些问题和其他类似问题后,我今天下午进行了一场快乐的追逐,试图解决使用文件作为同步(以及文件保存)方法的两个独立程序的问题。有点不寻常的情况,但它确实为我突出了“检查文件是否被锁定,如果不是,则打开它”方法的问题。

The problem is this: the file can becomelocked between the time that you check it and the time you actually open the file. Its really hard to track down the sporadic Cannot copy the file, because it's used by another processerror if you aren't looking for it too.

问题是:在您检查文件和实际打开文件之间,文件可能会锁定。很难追踪偶发的无法复制文件,因为如果您不寻找它,它会被另一个进程错误使用

The basic resolution is to just try to open the file inside a catch block so that if its locked, you can try again. That way there is no elapsed time between the check and the opening, the OS does them at the same time.

基本的解决方法是尝试在 catch 块内打开文件,以便如果它被锁定,您可以再试一次。这样在检查和打开之间没有经过的时间,操作系统会同时进行。

The code here uses File.Copy, but it works just as well with any of the static methods of the File class: File.Open, File.ReadAllText, File.WriteAllText, etc.

此处的代码使用 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;
    }
}

Another small note on parellelism:This is a synchronous method, which will block its thread both while waiting and while working on the thread. This is the simplest approach, but if the file remains locked for a long time your program may become unresponsive. Parellelism is too big a topic to go into in depth here, (and the number of ways you could set up asynchronous read/write is kind of preposterous) but here is one way it could be parellelized.

关于并行的另一个小注意事项:这是一个同步方法,它将在等待和处理线程时阻塞其线程。这是最简单的方法,但如果文件长时间保持锁定状态,您的程序可能会变得无响应。Parellelism 是一个太大的话题,无法在这里深入探讨(并且您可以设置异步读/写的方法数量有点荒谬)但这是一种可以并行化的方法。

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

And here is how it could be used:

这是它的使用方法:

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!