加快非现有网络共享的 File.Exists 速度

发布于 2024-07-30 09:08:51 字数 3343 浏览 3 评论 0原文

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

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

问题

  • 有没有办法缩短非现有网络共享的超时? (我确信当它们确实存在时,它们会很快回答,因此 1 秒的超时就可以了)

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

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

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

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


专家交流解决方案

首先,您可以在 IsDriveReady 函数中设置一个“超时”值。 我将其设置为 5 秒,但可以将其设置为适合您的任何值。

使用以下3种方法:

  1. 第一个是 WNetGetConnection API 函数,用于获取 驱动器的 UNC (\servername\share)
  2. 第二个是我们的主要方法:Button1_Click 事件
  3. 第三个是 IsDriveReady 函数,用于 ping 服务器。

这对我来说非常有用! 给你:

'此 API 函数将用于获取驱动器的 UNC 
  私有声明函数 WNetGetConnection Lib“mpr.dll”别名 _ 
  “WNetGetConnectionA”_ 
  (ByVal lpszLocalName 作为字符串,_ 
  ByVal lpszRemoteName 作为字符串,_ 
  ByRef cbRemoteName 作为 Int32) 作为 Int32 


  '这只是一个按钮单击事件 - 将代码添加到适当的事件中 
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) 处理 Button1.Click 

      Dim bIsReady As Boolean = False 

      对于每个 dri 作为 IO.DriveInfo In IO.DriveInfo.GetDrives() 

          '如果驱动器只是网络驱动器,则对其执行 ping 操作以查看其是否已准备好。 
          如果 dri.DriveType = IO.DriveType.Network 那么 

              '获取 UNC (\\服务器名称\共享)  
              ' dri.Name 返回的驱动器号 
              暗淡 UNC 作为字符串 = 空格(100) 
              WNetGetConnection(dri.Name.Substring(0, 2), UNC, 100) 

              '假设驱动器已映射 \\servername\share 
              ' 从 UNC 中解析出服务器名 
              调暗服务器作为字符串 = _ 
                   UNC.Trim().Substring(2, UNC.Trim().IndexOf("\", 2) - 2) 

              'Ping 服务器以查看其是否可用 
              bIsReady = IsDriveReady(服务器) 

          别的 
              bIsReady = dri.IsReady 

          万一 

          '只处理准备好的驱动器 
          如果 bIsReady = True 那么 
              '处理你的驱动器... 
              MsgBox(dri.Name & "已准备好:" & bIsReady) 

          万一 

      下一个 

      MsgBox("所有驱动器已处理") 

  结束子 

  私有函数 IsDriveReady(ByVal serverName As String) As Boolean 
      Dim bReturnStatus As Boolean = False 

      '*** 在此设置超时 *** 
      昏暗超时为整数 = 5 '5 秒 

      将 pingSender 调暗为新 System.Net.NetworkInformation.Ping() 
      暗淡选项作为新的 System.Net.NetworkInformation.PingOptions() 

      选项.DontFragment = True 

      '输入有效的IP地址 
      Dim ipAddressOrHostName As String = serverName 
      暗淡数据作为字符串=“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” 
      调暗缓冲区 As Byte() = System.Text.Encoding.ASCII.GetBytes(data) 
      昏暗回复为 System.Net.NetworkInformation.PingReply = _ 
                  pingSender.Send(ipAddressOrHostName,超时,缓冲区,选项) 

      如果reply.Status = Net.NetworkInformation.IPStatus.Success 那么 
          b返回状态 = True 

      万一 

      返回 bReturnStatus 
  结束功能 
  

I have to check if a set of file paths represent an existing file.

It works fine except when the path contains a network share on a machine that's not on the current network. In this case it takes a pretty long time (30 or 60 seconds) to timeout.

Questions

  • Is there a way to shorten the timeout for non existing network shares? (I'm certain that when they do exist they'll answer quickly, so a timeout of 1 sec would be fine)

  • Is there any other way to solve this issue without starting to cache and making the algorithm more complex? (ie, I already know these X network shares don't exist, skip the rest of the matching paths)

UPDATE: Using Threads work, not particularly elegant, though

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

This solution avoids the need for a thread per attempt, first check which drives are reachable and store that somewhere.


Experts exchange solution:

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:

  1. The first is the WNetGetConnection API function that gets the
    UNC (\servername\share) of the drive
  2. The second is our main method: The Button1_Click event
  3. The third is the IsDriveReady function that pings the server.

This worked great for me! Here you go:

'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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

独自←快乐 2024-08-06 09:08:52

简而言之,

  1. 构建可用驱动器列表。
  2. 尝试将驱动器号解析为 UNC 名称。
  3. 尝试 ping 驱动器。

编辑有关 Bill 的评论

如果 Google 不是引用者,EE 不会免费显示答案。 EE 的链接没有帮助。

OP 找到了我在原始答案中提到的文章,并且很友善地包含了 他的问题的解决方案的源代码

In a nutshell

  1. Build a list of available drives.
  2. Try to resolve the driveletter to an UNC name.
  3. Try to ping the drive.

Edit regarding Bill's comment

if Google is not the referer, EE doesn't show the answer for free. Links to EE are not helpful.

OP found the article I've mentioned in my original answer and was kind enough to include the source code for the solution to his question.

终止放荡 2024-08-06 09:08:52

另一个“线程解决方案”:

/// <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%确定,就不要使用它。

Another "thread solution":

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

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

There are some concerns about "drive not responding fast enough", so this is compromise between speed and "the truth". Don't you use It if you want to be sure 100%.

别把无礼当个性 2024-08-06 09:08:52

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

Use Threads to do the checks. I think that threads can be timed out.

心如狂蝶 2024-08-06 09:08:52

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

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

This worked GREAT for me!
Here's IsDriveReady() in C#:

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);
}
我ぃ本無心為│何有愛 2024-08-06 09:08:52

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

I found the pathExists with thread timeout function useful but finally realized that it needed a change from File.Exists to Directory.Exists to work correctly.

挽梦忆笙歌 2024-08-06 09:08:52

另一种解决方案是使用 CancellationToken

    public static async Task<bool> FileExistsWithTimeout(string path, int timeoutMilliseconds)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        Task<bool> directoryExistsTask = Task.Run(() => Path.Exists(path), token);

        if (await Task.WhenAny(directoryExistsTask, Task.Delay(timeoutMilliseconds, token)) == directoryExistsTask)
        {
            cts.Cancel();
            return await directoryExistsTask;
        }
        else
        {
            return false;
        }
    }

Also another solution is to use CancellationToken

    public static async Task<bool> FileExistsWithTimeout(string path, int timeoutMilliseconds)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;

        Task<bool> directoryExistsTask = Task.Run(() => Path.Exists(path), token);

        if (await Task.WhenAny(directoryExistsTask, Task.Delay(timeoutMilliseconds, token)) == directoryExistsTask)
        {
            cts.Cancel();
            return await directoryExistsTask;
        }
        else
        {
            return false;
        }
    }
陪我终i 2024-08-06 09:08:52

难道您不能仅使用 FileMonitor 控件来实现此目的,以便在删除该控件时触发事件吗? 然后你可以将bool设置为false;

Couldn't you just use FileMonitor control for this so that an event fires when it gets removed? Then you can set bool to false;

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文