等待文件在。net中解锁

时间:2022-08-23 18:00:00

What's the simplest way of blocking a thread until a file has been unlocked and is accessible for reading and renaming? For example, is there a WaitOnFile() somewhere in the .NET Framework?

在文件被解锁并可用于读取和重命名之前,阻塞线程的最简单方法是什么?例如,.NET框架中是否有WaitOnFile() ?

I have a service that uses a FileSystemWatcher to look for files that are to be transmitted to an FTP site, but the file created event fires before the other process has finished writing the file.

我有一个服务,它使用文件系统监视程序查找要传输到FTP站点的文件,但是在另一个进程写完文件之前,创建的文件会触发事件。

The ideal solution would have a timeout period so the thread doesn't hang forever before giving up.

理想的解决方案应该有一个超时周期,这样线程在放弃之前不会永远挂起。

Edit: After trying out some of the solutions below, I ended up changing the system so that all files wrote to Path.GetTempFileName(), then performed a File.Move() to the final location. As soon as the FileSystemWatcher event fired, the file was already complete.

编辑:在尝试了下面的一些解决方案之后,我最终更改了系统,使所有的文件都写入Path.GetTempFileName(),然后执行File.Move()到最终位置。一旦文件系统监视程序事件触发,文件就已经完成了。

15 个解决方案

#1


34  

This was the answer I gave on a related question:

这是我对一个相关问题的回答:

    /// <summary>
    /// Blocks until the file is not locked any more.
    /// </summary>
    /// <param name="fullPath"></param>
    bool WaitForFile(string fullPath)
    {
        int numTries = 0;
        while (true)
        {
            ++numTries;
            try
            {
                // Attempt to open the file exclusively.
                using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite, 
                    FileShare.None, 100))
                {
                    fs.ReadByte();

                    // If we got this far the file is ready
                    break;
                }
            }
            catch (Exception ex)
            {
                Log.LogWarning(
                   "WaitForFile {0} failed to get an exclusive lock: {1}", 
                    fullPath, ex.ToString());

                if (numTries > 10)
                {
                    Log.LogWarning(
                        "WaitForFile {0} giving up after 10 tries", 
                        fullPath);
                    return false;
                }

                // Wait for the lock to be released
                System.Threading.Thread.Sleep(500);
            }
        }

        Log.LogTrace("WaitForFile {0} returning true after {1} tries",
            fullPath, numTries);
        return true;
    }

#2


55  

Starting from Eric's answer, I included some improvements to make the code far more compact and reusable. Hope it's useful.

从Eric的回答开始,我包括了一些改进,使代码更加紧凑和可重用。希望它是有用的。

FileStream WaitForFile (string fullPath, FileMode mode, FileAccess access, FileShare share)
{
    for (int numTries = 0; numTries < 10; numTries++) {
        FileStream fs = null;
        try {
            fs = new FileStream (fullPath, mode, access, share);
            return fs;
        }
        catch (IOException) {
            if (fs != null) {
                fs.Dispose ();
            }
            Thread.Sleep (50);
        }
    }

    return null;
}

#3


15  

Here is a generic code to do this, independant from the file operation itself. This is an example on how to use it:

这里是一个通用代码,可以独立于文件操作本身。这是如何使用它的一个例子:

WrapSharingViolations(() => File.Delete(myFile));

or

WrapSharingViolations(() => File.Copy(mySourceFile, myDestFile));

You can also define the retry count, and the wait time between retries.

您还可以定义重试计数,以及重试之间的等待时间。

NOTE: Unfortunately, the underlying Win32 error (ERROR_SHARING_VIOLATION) is not exposed with .NET, so I have added a small hack function (IsSharingViolation) based on reflection mechanisms to check this.

注意:不幸的是,底层的Win32错误(error_sharing_breach)没有在. net中公开,因此我添加了一个基于反射机制的小hack函数(issharingconflict)来检查这一点。

    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action)
    {
        WrapSharingViolations(action, null, 10, 100);
    }

    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    /// <param name="exceptionsCallback">The exceptions callback. May be null.</param>
    /// <param name="retryCount">The retry count.</param>
    /// <param name="waitTime">The wait time in milliseconds.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action, WrapSharingViolationsExceptionsCallback exceptionsCallback, int retryCount, int waitTime)
    {
        if (action == null)
            throw new ArgumentNullException("action");

        for (int i = 0; i < retryCount; i++)
        {
            try
            {
                action();
                return;
            }
            catch (IOException ioe)
            {
                if ((IsSharingViolation(ioe)) && (i < (retryCount - 1)))
                {
                    bool wait = true;
                    if (exceptionsCallback != null)
                    {
                        wait = exceptionsCallback(ioe, i, retryCount, waitTime);
                    }
                    if (wait)
                    {
                        System.Threading.Thread.Sleep(waitTime);
                    }
                }
                else
                {
                    throw;
                }
            }
        }
    }

    /// <summary>
    /// Defines a sharing violation wrapper delegate.
    /// </summary>
    public delegate void WrapSharingViolationsCallback();

    /// <summary>
    /// Defines a sharing violation wrapper delegate for handling exception.
    /// </summary>
    public delegate bool WrapSharingViolationsExceptionsCallback(IOException ioe, int retry, int retryCount, int waitTime);

    /// <summary>
    /// Determines whether the specified exception is a sharing violation exception.
    /// </summary>
    /// <param name="exception">The exception. May not be null.</param>
    /// <returns>
    ///     <c>true</c> if the specified exception is a sharing violation exception; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsSharingViolation(IOException exception)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        int hr = GetHResult(exception, 0);
        return (hr == -2147024864); // 0x80070020 ERROR_SHARING_VIOLATION

    }

    /// <summary>
    /// Gets the HRESULT of the specified exception.
    /// </summary>
    /// <param name="exception">The exception to test. May not be null.</param>
    /// <param name="defaultValue">The default value in case of an error.</param>
    /// <returns>The HRESULT value.</returns>
    public static int GetHResult(IOException exception, int defaultValue)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        try
        {
            const string name = "HResult";
            PropertyInfo pi = exception.GetType().GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance); // CLR2
            if (pi == null)
            {
                pi = exception.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance); // CLR4
            }
            if (pi != null)
                return (int)pi.GetValue(exception, null);
        }
        catch
        {
        }
        return defaultValue;
    }

#4


12  

I threw together a helper class for these sorts of things. It will work if you have control over everything that would access the file. If you're expecting contention from a bunch of other things, then this is pretty worthless.

我为这类事情创建了一个助手类。如果您能够控制访问该文件的所有内容,那么它将起作用。如果你想从其他的事情中得到论点,那么这是毫无价值的。

using System;
using System.IO;
using System.Threading;

/// <summary>
/// This is a wrapper aroung a FileStream.  While it is not a Stream itself, it can be cast to
/// one (keep in mind that this might throw an exception).
/// </summary>
public class SafeFileStream: IDisposable
{
    #region Private Members
    private Mutex m_mutex;
    private Stream m_stream;
    private string m_path;
    private FileMode m_fileMode;
    private FileAccess m_fileAccess;
    private FileShare m_fileShare;
    #endregion//Private Members

    #region Constructors
    public SafeFileStream(string path, FileMode mode, FileAccess access, FileShare share)
    {
        m_mutex = new Mutex(false, String.Format("Global\\{0}", path.Replace('\\', '/')));
        m_path = path;
        m_fileMode = mode;
        m_fileAccess = access;
        m_fileShare = share;
    }
    #endregion//Constructors

    #region Properties
    public Stream UnderlyingStream
    {
        get
        {
            if (!IsOpen)
                throw new InvalidOperationException("The underlying stream does not exist - try opening this stream.");
            return m_stream;
        }
    }

    public bool IsOpen
    {
        get { return m_stream != null; }
    }
    #endregion//Properties

    #region Functions
    /// <summary>
    /// Opens the stream when it is not locked.  If the file is locked, then
    /// </summary>
    public void Open()
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        m_mutex.WaitOne();
        m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
    }

    public bool TryOpen(TimeSpan span)
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        if (m_mutex.WaitOne(span))
        {
            m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
            return true;
        }
        else
            return false;
    }

    public void Close()
    {
        if (m_stream != null)
        {
            m_stream.Close();
            m_stream = null;
            m_mutex.ReleaseMutex();
        }
    }

    public void Dispose()
    {
        Close();
        GC.SuppressFinalize(this);
    }

    public static explicit operator Stream(SafeFileStream sfs)
    {
        return sfs.UnderlyingStream;
    }
    #endregion//Functions
}

It works using a named mutex. Those wishing to access the file attempt to acquire control of the named mutex, which shares the name of the file (with the '\'s turned into '/'s). You can either use Open(), which will stall until the mutex is accessible or you can use TryOpen(TimeSpan), which tries to acquire the mutex for the given duration and returns false if it cannot acquire within the time span. This should most likely be used inside a using block, to ensure that locks are released properly, and the stream (if open) will be properly disposed when this object is disposed.

它使用一个命名的互斥锁。那些希望访问该文件的人试图获得命名互斥对象的控制权,该互斥对象共享该文件的名称('\'s变成'/'s)。您可以使用Open(),它将一直保持到互斥对象可访问为止,也可以使用TryOpen(TimeSpan),它试图在给定的持续时间内获取互斥对象,如果在时间范围内无法获取,则返回false。这很可能在using块中使用,以确保正确释放锁,并且在处理该对象时,流(如果打开)将被正确处理。

I did a quick test with ~20 things to do various reads/writes of the file and saw no corruption. Obviously it's not very advanced, but it should work for the majority of simple cases.

我用大约20件事做了一个快速测试,以完成对文件的各种读/写操作,没有发现任何损坏。显然它不是很高级,但是它应该适用于大多数简单的情况。

#5


5  

For this particular application directly observing the file will inevitably lead to a hard to trace bug, especially when the file size increases. Here are two different strategies that will work.

对于这个特定的应用程序,直接观察文件将不可避免地导致难以跟踪的错误,特别是当文件大小增加时。下面是两种不同的策略。

  • Ftp two files but only watch one. For example send the files important.txt and important.finish. Only watch for the finish file but process the txt.
  • Ftp两个文件,但只看一个。例如,发送重要的文件。txt和important.finish。只注意完成文件,但处理txt文件。
  • FTP one file but rename it when done. For example send important.wait and have the sender rename it to important.txt when finished.
  • 一个文件,但重命名时,完成。例如发送重要。等待并让发送者将其重命名为important。txt完成后。

Good luck!

好运!

#6


4  

One of the techniques I used some time back was to write my own function. Basically catch the exception and retry using a timer which you can fire for a specified duration. If there is a better way, please share.

一段时间以前我使用的技巧之一就是写我自己的函数。基本上捕获异常并使用计时器重试,您可以在指定的持续时间内触发该计时器。如果有更好的方法,请分享。

#7


3  

From MSDN:

从MSDN:

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

一旦创建了文件,OnCreated事件就会被引发。如果文件被复制或转移到监视目录中,OnCreated事件将立即引发,然后是一个或多个OnChanged事件。

Your FileSystemWatcher could be modified so that it doesn't do its read/rename during the "OnCreated" event, but rather:

可以修改您的文件系统监视程序,使其不会在“OnCreated”事件期间进行读取/重命名,而是:

  1. Spanws a thread that polls the file status until it is not locked (using a FileInfo object)
  2. span一个线程,该线程轮询文件状态直到它没有被锁定(使用FileInfo对象)
  3. Calls back into the service to process the file as soon as it determines the file is no longer locked and is ready to go
  4. 一旦确定文件不再被锁定并准备就绪,就调用回服务来处理该文件

#8


2  

In most cases simple approach like @harpo suggested will work. You can develop more sophisticated code using this approach:

在大多数情况下,像@harpo所建议的简单方法是有效的。您可以使用这种方法开发更复杂的代码:

  • Find all opened handles for selected file using SystemHandleInformation\SystemProcessInformation
  • 使用SystemHandleInformation\SystemProcessInformation查找所选文件的所有打开的句柄。
  • Subclass WaitHandle class to gain access to it's internal handle
  • 子类WaitHandle类以获得对其内部句柄的访问
  • Pass found handles wrapped in subclassed WaitHandle to WaitHandle.WaitAny method
  • 将包在子类WaitHandle中的手柄传递给WaitHandle。WaitAny方法

#9


2  

Ad to transfer process trigger file SameNameASTrasferedFile.trg that is created after file transmission is completed.

传输过程触发文件sam珐琅strasferedfile。完成文件传输后创建的trg。

Then setup FileSystemWatcher that will fire event only on *.trg file.

然后安装文件系统监视程序,只在*上触发事件。丹文件。

#10


1  

I don't know what you're using to determine the file's lock status, but something like this should do it.

我不知道您用什么来确定文件的锁状态,但是像这样的东西应该可以做到。

while (true)
{
    try {
        stream = File.Open( fileName, fileMode );
        break;
    }
    catch( FileIOException ) {

        // check whether it's a lock problem

        Thread.Sleep( 100 );
    }
}

#11


0  

A possible solution would be, to combine a filesystemwatcher with some polling,

一种可能的解决方案是,将文件系统监视程序与一些轮询相结合,

get Notified for every Change on a File, and when getting notified check if it is locked as stated in the currently accepted answer: https://*.com/a/50800/6754146 The code for opening the filestream is copied from the answer and slightly modified:

获得对文件的每一个更改的通知,并在收到通知检查时,如果它被锁定为当前已接受的答案:https://*.com/a/50800/6754146,打开filestream的代码将从答案中复制并稍微修改:

public static void CheckFileLock(string directory, string filename, Func<Task> callBack)
{
    var watcher = new FileSystemWatcher(directory, filename);
    FileSystemEventHandler check = 
        async (sender, eArgs) =>
    {
        string fullPath = Path.Combine(directory, filename);
        try
        {
            // Attempt to open the file exclusively.
            using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite,
                    FileShare.None, 100))
            {
                fs.ReadByte();
                watcher.EnableRaisingEvents = false;
                // If we got this far the file is ready
            }
            watcher.Dispose();
            await callBack();
        }
        catch (IOException) { }
    };
    watcher.NotifyFilter = NotifyFilters.LastWrite;
    watcher.IncludeSubdirectories = false;
    watcher.EnableRaisingEvents = true;
    //Attach the checking to the changed method, 
    //on every change it gets checked once
    watcher.Changed += check;
    //Initially do a check for the case it is already released
    check(null, null);
}

With this way you can Check for a file if its locked and get notified when its closed over the specified callback, this way you avoid the overly aggressive polling and only do the work when it may be actually be closed

通过这种方式,您可以检查文件是否被锁定,并在它在指定的回调上关闭时得到通知,这样您就可以避免过于主动的轮询,并且只在它实际上可能被关闭时才进行工作

#12


-1  

I do it the same way as Gulzar, just keep trying with a loop.

我用和Gulzar一样的方法,继续循环。

In fact I don't even bother with the file system watcher. Polling a network drive for new files once a minute is cheap.

事实上,我甚至不关心文件系统观察者。每分钟为新文件轮询一次网络驱动器是很便宜的。

#13


-1  

Simply use the Changed event with the NotifyFilter NotifyFilters.LastWrite:

只需使用NotifyFilter NotifyFilters.LastWrite:

var watcher = new FileSystemWatcher {
      Path = @"c:\temp\test",
      Filter = "*.xml",
      NotifyFilter = NotifyFilters.LastWrite
};
watcher.Changed += watcher_Changed; 
watcher.EnableRaisingEvents = true;

#14


-1  

I ran into a similar issue when adding an outlook attachment. "Using" saved the day.

我在添加outlook附件时遇到了类似的问题。“使用”拯救了一天。

string fileName = MessagingBLL.BuildPropertyAttachmentFileName(currProp);

                //create a temporary file to send as the attachment
                string pathString = Path.Combine(Path.GetTempPath(), fileName);

                //dirty trick to make sure locks are released on the file.
                using (System.IO.File.Create(pathString)) { }

                mailItem.Subject = MessagingBLL.PropertyAttachmentSubject;
                mailItem.Attachments.Add(pathString, Outlook.OlAttachmentType.olByValue, Type.Missing, Type.Missing);

#15


-3  

How about this as an option:

作为一个选择怎么样:

private void WaitOnFile(string fileName)
{
    FileInfo fileInfo = new FileInfo(fileName);
    for (long size = -1; size != fileInfo.Length; fileInfo.Refresh())
    {
        size = fileInfo.Length;
        System.Threading.Thread.Sleep(1000);
    }
}

Of course if the filesize is preallocated on the create you'd get a false positive.

当然,如果在create上预先分配了filesize,就会得到一个假正数。

#1


34  

This was the answer I gave on a related question:

这是我对一个相关问题的回答:

    /// <summary>
    /// Blocks until the file is not locked any more.
    /// </summary>
    /// <param name="fullPath"></param>
    bool WaitForFile(string fullPath)
    {
        int numTries = 0;
        while (true)
        {
            ++numTries;
            try
            {
                // Attempt to open the file exclusively.
                using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite, 
                    FileShare.None, 100))
                {
                    fs.ReadByte();

                    // If we got this far the file is ready
                    break;
                }
            }
            catch (Exception ex)
            {
                Log.LogWarning(
                   "WaitForFile {0} failed to get an exclusive lock: {1}", 
                    fullPath, ex.ToString());

                if (numTries > 10)
                {
                    Log.LogWarning(
                        "WaitForFile {0} giving up after 10 tries", 
                        fullPath);
                    return false;
                }

                // Wait for the lock to be released
                System.Threading.Thread.Sleep(500);
            }
        }

        Log.LogTrace("WaitForFile {0} returning true after {1} tries",
            fullPath, numTries);
        return true;
    }

#2


55  

Starting from Eric's answer, I included some improvements to make the code far more compact and reusable. Hope it's useful.

从Eric的回答开始,我包括了一些改进,使代码更加紧凑和可重用。希望它是有用的。

FileStream WaitForFile (string fullPath, FileMode mode, FileAccess access, FileShare share)
{
    for (int numTries = 0; numTries < 10; numTries++) {
        FileStream fs = null;
        try {
            fs = new FileStream (fullPath, mode, access, share);
            return fs;
        }
        catch (IOException) {
            if (fs != null) {
                fs.Dispose ();
            }
            Thread.Sleep (50);
        }
    }

    return null;
}

#3


15  

Here is a generic code to do this, independant from the file operation itself. This is an example on how to use it:

这里是一个通用代码,可以独立于文件操作本身。这是如何使用它的一个例子:

WrapSharingViolations(() => File.Delete(myFile));

or

WrapSharingViolations(() => File.Copy(mySourceFile, myDestFile));

You can also define the retry count, and the wait time between retries.

您还可以定义重试计数,以及重试之间的等待时间。

NOTE: Unfortunately, the underlying Win32 error (ERROR_SHARING_VIOLATION) is not exposed with .NET, so I have added a small hack function (IsSharingViolation) based on reflection mechanisms to check this.

注意:不幸的是,底层的Win32错误(error_sharing_breach)没有在. net中公开,因此我添加了一个基于反射机制的小hack函数(issharingconflict)来检查这一点。

    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action)
    {
        WrapSharingViolations(action, null, 10, 100);
    }

    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    /// <param name="exceptionsCallback">The exceptions callback. May be null.</param>
    /// <param name="retryCount">The retry count.</param>
    /// <param name="waitTime">The wait time in milliseconds.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action, WrapSharingViolationsExceptionsCallback exceptionsCallback, int retryCount, int waitTime)
    {
        if (action == null)
            throw new ArgumentNullException("action");

        for (int i = 0; i < retryCount; i++)
        {
            try
            {
                action();
                return;
            }
            catch (IOException ioe)
            {
                if ((IsSharingViolation(ioe)) && (i < (retryCount - 1)))
                {
                    bool wait = true;
                    if (exceptionsCallback != null)
                    {
                        wait = exceptionsCallback(ioe, i, retryCount, waitTime);
                    }
                    if (wait)
                    {
                        System.Threading.Thread.Sleep(waitTime);
                    }
                }
                else
                {
                    throw;
                }
            }
        }
    }

    /// <summary>
    /// Defines a sharing violation wrapper delegate.
    /// </summary>
    public delegate void WrapSharingViolationsCallback();

    /// <summary>
    /// Defines a sharing violation wrapper delegate for handling exception.
    /// </summary>
    public delegate bool WrapSharingViolationsExceptionsCallback(IOException ioe, int retry, int retryCount, int waitTime);

    /// <summary>
    /// Determines whether the specified exception is a sharing violation exception.
    /// </summary>
    /// <param name="exception">The exception. May not be null.</param>
    /// <returns>
    ///     <c>true</c> if the specified exception is a sharing violation exception; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsSharingViolation(IOException exception)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        int hr = GetHResult(exception, 0);
        return (hr == -2147024864); // 0x80070020 ERROR_SHARING_VIOLATION

    }

    /// <summary>
    /// Gets the HRESULT of the specified exception.
    /// </summary>
    /// <param name="exception">The exception to test. May not be null.</param>
    /// <param name="defaultValue">The default value in case of an error.</param>
    /// <returns>The HRESULT value.</returns>
    public static int GetHResult(IOException exception, int defaultValue)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        try
        {
            const string name = "HResult";
            PropertyInfo pi = exception.GetType().GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance); // CLR2
            if (pi == null)
            {
                pi = exception.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance); // CLR4
            }
            if (pi != null)
                return (int)pi.GetValue(exception, null);
        }
        catch
        {
        }
        return defaultValue;
    }

#4


12  

I threw together a helper class for these sorts of things. It will work if you have control over everything that would access the file. If you're expecting contention from a bunch of other things, then this is pretty worthless.

我为这类事情创建了一个助手类。如果您能够控制访问该文件的所有内容,那么它将起作用。如果你想从其他的事情中得到论点,那么这是毫无价值的。

using System;
using System.IO;
using System.Threading;

/// <summary>
/// This is a wrapper aroung a FileStream.  While it is not a Stream itself, it can be cast to
/// one (keep in mind that this might throw an exception).
/// </summary>
public class SafeFileStream: IDisposable
{
    #region Private Members
    private Mutex m_mutex;
    private Stream m_stream;
    private string m_path;
    private FileMode m_fileMode;
    private FileAccess m_fileAccess;
    private FileShare m_fileShare;
    #endregion//Private Members

    #region Constructors
    public SafeFileStream(string path, FileMode mode, FileAccess access, FileShare share)
    {
        m_mutex = new Mutex(false, String.Format("Global\\{0}", path.Replace('\\', '/')));
        m_path = path;
        m_fileMode = mode;
        m_fileAccess = access;
        m_fileShare = share;
    }
    #endregion//Constructors

    #region Properties
    public Stream UnderlyingStream
    {
        get
        {
            if (!IsOpen)
                throw new InvalidOperationException("The underlying stream does not exist - try opening this stream.");
            return m_stream;
        }
    }

    public bool IsOpen
    {
        get { return m_stream != null; }
    }
    #endregion//Properties

    #region Functions
    /// <summary>
    /// Opens the stream when it is not locked.  If the file is locked, then
    /// </summary>
    public void Open()
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        m_mutex.WaitOne();
        m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
    }

    public bool TryOpen(TimeSpan span)
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        if (m_mutex.WaitOne(span))
        {
            m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
            return true;
        }
        else
            return false;
    }

    public void Close()
    {
        if (m_stream != null)
        {
            m_stream.Close();
            m_stream = null;
            m_mutex.ReleaseMutex();
        }
    }

    public void Dispose()
    {
        Close();
        GC.SuppressFinalize(this);
    }

    public static explicit operator Stream(SafeFileStream sfs)
    {
        return sfs.UnderlyingStream;
    }
    #endregion//Functions
}

It works using a named mutex. Those wishing to access the file attempt to acquire control of the named mutex, which shares the name of the file (with the '\'s turned into '/'s). You can either use Open(), which will stall until the mutex is accessible or you can use TryOpen(TimeSpan), which tries to acquire the mutex for the given duration and returns false if it cannot acquire within the time span. This should most likely be used inside a using block, to ensure that locks are released properly, and the stream (if open) will be properly disposed when this object is disposed.

它使用一个命名的互斥锁。那些希望访问该文件的人试图获得命名互斥对象的控制权,该互斥对象共享该文件的名称('\'s变成'/'s)。您可以使用Open(),它将一直保持到互斥对象可访问为止,也可以使用TryOpen(TimeSpan),它试图在给定的持续时间内获取互斥对象,如果在时间范围内无法获取,则返回false。这很可能在using块中使用,以确保正确释放锁,并且在处理该对象时,流(如果打开)将被正确处理。

I did a quick test with ~20 things to do various reads/writes of the file and saw no corruption. Obviously it's not very advanced, but it should work for the majority of simple cases.

我用大约20件事做了一个快速测试,以完成对文件的各种读/写操作,没有发现任何损坏。显然它不是很高级,但是它应该适用于大多数简单的情况。

#5


5  

For this particular application directly observing the file will inevitably lead to a hard to trace bug, especially when the file size increases. Here are two different strategies that will work.

对于这个特定的应用程序,直接观察文件将不可避免地导致难以跟踪的错误,特别是当文件大小增加时。下面是两种不同的策略。

  • Ftp two files but only watch one. For example send the files important.txt and important.finish. Only watch for the finish file but process the txt.
  • Ftp两个文件,但只看一个。例如,发送重要的文件。txt和important.finish。只注意完成文件,但处理txt文件。
  • FTP one file but rename it when done. For example send important.wait and have the sender rename it to important.txt when finished.
  • 一个文件,但重命名时,完成。例如发送重要。等待并让发送者将其重命名为important。txt完成后。

Good luck!

好运!

#6


4  

One of the techniques I used some time back was to write my own function. Basically catch the exception and retry using a timer which you can fire for a specified duration. If there is a better way, please share.

一段时间以前我使用的技巧之一就是写我自己的函数。基本上捕获异常并使用计时器重试,您可以在指定的持续时间内触发该计时器。如果有更好的方法,请分享。

#7


3  

From MSDN:

从MSDN:

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

一旦创建了文件,OnCreated事件就会被引发。如果文件被复制或转移到监视目录中,OnCreated事件将立即引发,然后是一个或多个OnChanged事件。

Your FileSystemWatcher could be modified so that it doesn't do its read/rename during the "OnCreated" event, but rather:

可以修改您的文件系统监视程序,使其不会在“OnCreated”事件期间进行读取/重命名,而是:

  1. Spanws a thread that polls the file status until it is not locked (using a FileInfo object)
  2. span一个线程,该线程轮询文件状态直到它没有被锁定(使用FileInfo对象)
  3. Calls back into the service to process the file as soon as it determines the file is no longer locked and is ready to go
  4. 一旦确定文件不再被锁定并准备就绪,就调用回服务来处理该文件

#8


2  

In most cases simple approach like @harpo suggested will work. You can develop more sophisticated code using this approach:

在大多数情况下,像@harpo所建议的简单方法是有效的。您可以使用这种方法开发更复杂的代码:

  • Find all opened handles for selected file using SystemHandleInformation\SystemProcessInformation
  • 使用SystemHandleInformation\SystemProcessInformation查找所选文件的所有打开的句柄。
  • Subclass WaitHandle class to gain access to it's internal handle
  • 子类WaitHandle类以获得对其内部句柄的访问
  • Pass found handles wrapped in subclassed WaitHandle to WaitHandle.WaitAny method
  • 将包在子类WaitHandle中的手柄传递给WaitHandle。WaitAny方法

#9


2  

Ad to transfer process trigger file SameNameASTrasferedFile.trg that is created after file transmission is completed.

传输过程触发文件sam珐琅strasferedfile。完成文件传输后创建的trg。

Then setup FileSystemWatcher that will fire event only on *.trg file.

然后安装文件系统监视程序,只在*上触发事件。丹文件。

#10


1  

I don't know what you're using to determine the file's lock status, but something like this should do it.

我不知道您用什么来确定文件的锁状态,但是像这样的东西应该可以做到。

while (true)
{
    try {
        stream = File.Open( fileName, fileMode );
        break;
    }
    catch( FileIOException ) {

        // check whether it's a lock problem

        Thread.Sleep( 100 );
    }
}

#11


0  

A possible solution would be, to combine a filesystemwatcher with some polling,

一种可能的解决方案是,将文件系统监视程序与一些轮询相结合,

get Notified for every Change on a File, and when getting notified check if it is locked as stated in the currently accepted answer: https://*.com/a/50800/6754146 The code for opening the filestream is copied from the answer and slightly modified:

获得对文件的每一个更改的通知,并在收到通知检查时,如果它被锁定为当前已接受的答案:https://*.com/a/50800/6754146,打开filestream的代码将从答案中复制并稍微修改:

public static void CheckFileLock(string directory, string filename, Func<Task> callBack)
{
    var watcher = new FileSystemWatcher(directory, filename);
    FileSystemEventHandler check = 
        async (sender, eArgs) =>
    {
        string fullPath = Path.Combine(directory, filename);
        try
        {
            // Attempt to open the file exclusively.
            using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite,
                    FileShare.None, 100))
            {
                fs.ReadByte();
                watcher.EnableRaisingEvents = false;
                // If we got this far the file is ready
            }
            watcher.Dispose();
            await callBack();
        }
        catch (IOException) { }
    };
    watcher.NotifyFilter = NotifyFilters.LastWrite;
    watcher.IncludeSubdirectories = false;
    watcher.EnableRaisingEvents = true;
    //Attach the checking to the changed method, 
    //on every change it gets checked once
    watcher.Changed += check;
    //Initially do a check for the case it is already released
    check(null, null);
}

With this way you can Check for a file if its locked and get notified when its closed over the specified callback, this way you avoid the overly aggressive polling and only do the work when it may be actually be closed

通过这种方式,您可以检查文件是否被锁定,并在它在指定的回调上关闭时得到通知,这样您就可以避免过于主动的轮询,并且只在它实际上可能被关闭时才进行工作

#12


-1  

I do it the same way as Gulzar, just keep trying with a loop.

我用和Gulzar一样的方法,继续循环。

In fact I don't even bother with the file system watcher. Polling a network drive for new files once a minute is cheap.

事实上,我甚至不关心文件系统观察者。每分钟为新文件轮询一次网络驱动器是很便宜的。

#13


-1  

Simply use the Changed event with the NotifyFilter NotifyFilters.LastWrite:

只需使用NotifyFilter NotifyFilters.LastWrite:

var watcher = new FileSystemWatcher {
      Path = @"c:\temp\test",
      Filter = "*.xml",
      NotifyFilter = NotifyFilters.LastWrite
};
watcher.Changed += watcher_Changed; 
watcher.EnableRaisingEvents = true;

#14


-1  

I ran into a similar issue when adding an outlook attachment. "Using" saved the day.

我在添加outlook附件时遇到了类似的问题。“使用”拯救了一天。

string fileName = MessagingBLL.BuildPropertyAttachmentFileName(currProp);

                //create a temporary file to send as the attachment
                string pathString = Path.Combine(Path.GetTempPath(), fileName);

                //dirty trick to make sure locks are released on the file.
                using (System.IO.File.Create(pathString)) { }

                mailItem.Subject = MessagingBLL.PropertyAttachmentSubject;
                mailItem.Attachments.Add(pathString, Outlook.OlAttachmentType.olByValue, Type.Missing, Type.Missing);

#15


-3  

How about this as an option:

作为一个选择怎么样:

private void WaitOnFile(string fileName)
{
    FileInfo fileInfo = new FileInfo(fileName);
    for (long size = -1; size != fileInfo.Length; fileInfo.Refresh())
    {
        size = fileInfo.Length;
        System.Threading.Thread.Sleep(1000);
    }
}

Of course if the filesize is preallocated on the create you'd get a false positive.

当然,如果在create上预先分配了filesize,就会得到一个假正数。