Ускорить File.Exists для несуществующих сетевых ресурсов - PullRequest
32 голосов
/ 05 августа 2009

Я должен проверить, представляет ли набор путей к файлам существующий файл.

Работает нормально, кроме случаев, когда путь содержит общий сетевой ресурс на машине, которой нет в текущей сети. В этом случае тайм-аут занимает довольно много времени (30 или 60 секунд).

Вопросы * * 1005

  • Есть ли способ сократить время ожидания для несуществующих сетевых ресурсов? (Я уверен, что, когда они существуют, они ответят быстро, поэтому время ожидания в 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;
}

Это решение позволяет избежать необходимости в потоке на попытку, сначала проверьте, какие диски доступны, и сохраните их где-нибудь.


Эксперты обмениваются решениями :

Прежде всего, есть значение «timeout», которое вы можете установить в функции IsDriveReady. У меня он установлен на 5 секунд, но он настроен на то, что вам подходит.

3 метода используются ниже:

  1. Первая - это функция API WNetGetConnection, которая получает UNC (\ имя_сервера \ share) диска
  2. Второй наш основной метод: событие Button1_Click
  3. Третья функция IsDriveReady, которая проверяет связь с сервером.

Это отлично сработало для меня! Вот, пожалуйста:

'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

Ответы [ 6 ]

7 голосов
/ 05 августа 2009

В двух словах

  1. Создание списка доступных дисков.
  2. Попробуйте преобразовать букву диска в имя UNC.
  3. Попробуйте ping диск.

Редактировать относительно комментария Билла

если Google не является реферером, EE не показывает ответ бесплатно. Ссылки на EE не помогают.

ОП нашел статью, о которой я упоминал в своем первоначальном ответе, и был любезен включить исходный код решения в свой вопрос.

4 голосов
/ 05 августа 2009

Используйте темы для проверки. Я думаю, что темы могут быть заблокированы.

1 голос
/ 27 февраля 2011

Это работало здорово для меня!
Вот IsDriveReady () в 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);
}
0 голосов
/ 05 октября 2018

Еще одно «решение проблемы»:

/// <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%.

0 голосов
/ 29 июня 2013

Я обнаружил, что pathExists с функцией тайм-аута потока полезен, но, наконец, понял, что для корректной работы необходимо изменить File.Exists на Directory.Exists.

0 голосов
/ 07 августа 2009

Не могли бы вы просто использовать элемент управления FileMonitor для этого, чтобы событие срабатывало при его удалении? Тогда вы можете установить bool в false;

...