关于 c#:Speed up File.Exists 用于非现有网络共享

Speed up File.Exists for non existing network shares

我必须检查一组文件路径是否代表现有文件。

它工作正常,除非路径包含不在当前网络上的计算机上的网络共享。在这种情况下,超时需要相当长的时间(30 或 60 秒)。

问题

  • 有没有办法缩短非现有网络共享的超时时间? (我敢肯定,当它们确实存在时,它们会迅速回答,所以 1 秒的超时就可以了)

  • 有没有其他方法可以在不开始缓存并使算法更复杂的情况下解决这个问题? (即,我已经知道这些 X 网络共享不存在,跳过其余匹配路径)

更新:使用线程工作,虽然不是特别优雅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public bool pathExists(string path)
{
    bool exists = true;
    Thread t = new Thread
    (
        new ThreadStart(delegate ()
        {
            exists = System.IO.File.Exists(path);
        })
    );
    t.Start();
    bool completed = t.Join(500); //half a sec of timeout
    if (!completed) { exists = false; t.Abort(); }
    return exists;
}

这个解决方案避免了每次尝试都需要一个线程,首先检查哪些驱动器可以访问并将其存储在某个地方。

专家交流解决方案:

First of all, there is a"timeout" value that you can set in the IsDriveReady function. I have it set for 5 seconds, but set it for whatever works for you.

3 methods are used below:

  • The first is the WNetGetConnection API function that gets the
    UNC (\\servername\\share) of the drive
  • The second is our main method: The Button1_Click event
  • The third is the IsDriveReady function that pings the server.
  • This worked great for me! Here you go:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    'This API Function will be used to get the UNC of the drive
    Private Declare Function WNetGetConnection Lib"mpr.dll" Alias _
    "WNetGetConnectionA" _
    (ByVal lpszLocalName As String, _
    ByVal lpszRemoteName As String, _
    ByRef cbRemoteName As Int32) As Int32


    '
    This is just a button click event - add code to your appropriate event
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim bIsReady As Boolean = False

        For Each dri As IO.DriveInfo In IO.DriveInfo.GetDrives()

            'If the drive is a Network drive only, then ping it to see if it's ready.
            If dri.DriveType = IO.DriveType.Network Then

                'Get the UNC (\\\\servername\\share) for the
                '
       drive letter returned by dri.Name
                Dim UNC As String = Space(100)
                WNetGetConnection(dri.Name.Substring(0, 2), UNC, 100)

                'Presuming the drive is mapped \\\\servername\\share
                '
       Parse the servername out of the UNC
                Dim server As String = _
                     UNC.Trim().Substring(2, UNC.Trim().IndexOf("", 2) - 2)

                'Ping the server to see if it is available
                bIsReady = IsDriveReady(server)

            Else
                bIsReady = dri.IsReady

            End If

            '
    Only process drives that are ready
            If bIsReady = True Then
                'Process your drive...
                MsgBox(dri.Name &" is ready: " & bIsReady)

            End If

        Next

        MsgBox("All drives processed")

    End Sub

    Private Function IsDriveReady(ByVal serverName As String) As Boolean
        Dim bReturnStatus As Boolean = False

        '
    ***  SET YOUR TIMEOUT HERE  ***
        Dim timeout As Integer = 5    '5 seconds

        Dim pingSender As New System.Net.NetworkInformation.Ping()
        Dim options As New System.Net.NetworkInformation.PingOptions()

        options.DontFragment = True

        '
    Enter a valid ip address
        Dim ipAddressOrHostName As String = serverName
        Dim data As String ="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        Dim buffer As Byte() = System.Text.Encoding.ASCII.GetBytes(data)
        Dim reply As System.Net.NetworkInformation.PingReply = _
                    pingSender.Send(ipAddressOrHostName, timeout, buffer, options)

        If reply.Status = Net.NetworkInformation.IPStatus.Success Then
            bReturnStatus = True

        End If

        Return bReturnStatus
    End Function


    简而言之

  • 建立可用驱动器的列表。
  • 尝试将驱动器号解析为 UNC 名称。
  • 尝试 ping 驱动器。
  • 编辑关于比尔的评论

    如果 Google 不是推荐人,EE 不会免费显示答案。指向 EE 的链接没有帮助。

    OP 找到了我在原始答案中提到的文章,并且非常友好地提供了解决他问题的源代码。


    另一个"线程解决方案":

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /// <sumary>Check if file exists with timeout</sumary>
    /// <param name="fileInfo">source</param>
    /// <param name="millisecondsTimeout">The number of milliseconds to wait,
    ///  or <see cref="System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param>
    /// <returns>Gets a value indicating whether a file exists.</returns>
    public static bool Exists(this FileInfo fileInfo, int millisecondsTimeout)
    {
        var task = new Task<bool>(() => fileInfo.Exists);
        task.Start();
        return task.Wait(millisecondsTimeout) && task.Result;
    }

    来源:http://www.jonathanantoine.com/2011/08/18/faster-file-exists/

    有些人担心"驱动器响应不够快",所以这是速度和"真相"之间的妥协。如果您想确保 100%,请不要使用它。


    使用线程进行检查。我认为线程可以超时。


    这对我来说非常有用!
    这是 C# 中的 IsDriveReady():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    using System.Net;
    private bool IsDriveReady(string serverName)
    {
       // ***  SET YOUR TIMEOUT HERE  ***    
       int timeout = 5;    // 5 seconds
       System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping();
       System.Net.NetworkInformation.PingOptions options = new System.Net.NetworkInformation.PingOptions();
       options.DontFragment = true;      
       // Enter a valid ip address    
       string ipAddressOrHostName = serverName;
       string data ="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
       byte[] buffer = System.Text.Encoding.ASCII.GetBytes(data);
       System.Net.NetworkInformation.PingReply reply = pingSender.Send(ipAddressOrHostName, timeout, buffer, options);
       return (reply.Status == System.Net.NetworkInformation.IPStatus.Success);
    }

    我发现带有线程超时功能的 pathExists 很有用,但最终意识到它需要从 File.Exists 更改为 Directory.Exists 才能正常工作。


    您不能为此使用 FileMonitor 控件,以便在删除事件时触发事件吗?然后你可以将 bool 设置为 false;