none
Parcours d'un repertoire : vitesse d'execution et traitement des cas d'echec. RRS feed

  • Question

  • Bonjour a tous,

    dans le cadre de notre projet (sauvegarde à distance), nous devons parcourir le plus rapidement possible
    un chemin donné (en général une lettre reseau), et executer un traitement sur chaque repertoire / fichier,
    sachant que nous avons besoin de certaines infos (attribut, taille, date ...).

    Ma premiere idee etait donc d'utiliser la méthode  DirectoryInfo.GetFiles(string,SearchOption) retournant un tableau
    de System.IO.FileInfo. Voici le code en exemple :

    Dim dir as New DirectoryInfo("V:")
    
    Dim FSInfo AS FileInfo()
    
    FSInfo = dir.GetFiles("*", SearchOption.AllDirectories)<br/>
    
    

    Avantage : on récupère un FileInfo() très rapidement (4s pour 65000 fichiers)
    Inconvénients : il est impossible de récuperer des valeurs si une exception est générée (chemin trop long, accès interdit) ...
    Dans ce cas, un try catch nous permet juste de récupérer l'erreur, mais pas un FileInfo() sans les fichiers posant problème.
    De plus cette méthode ne nous renvoye pas les répertoires ...



    Voila, nous avons donc changé de méthode, et utilisé une fonction récursive :
    '//appel général
    
    Dim dir AS New DirectoryInfo("V:")
    
    ListeRep(dir)
    
    
    
    
    
    '//Fonction ListeRep
    
    Public Sub ListeRep(ByRef oRep As DirectoryInfo)
    
       'Test : est ce que le repertoire existe ?
    
        If Not oRep.Exists Then
    
            Return
    
        End If
    
    <br/><br/><br/>
    
        'On parcours tous les fichiers du rep
    
        try
    
          For Each oFich As FileInfo In oRep.GetFiles()
    
              'traitement
    
          End
    
        Catch ex As Exception
    
              'traitement si echec
    
        End Try
    
        
    
       'On parcours tous les sous repertoires (recurrence)
    
       try
    
          For Each oSousRep As DirectoryInfo In oRep.GetDirectories()
    
              ListeRep(oSousRep)
    
          Next
    
       Catch ex As Exception
    
              'traitement si echec
    
        End Try
    
    
    
    End Sub
    
    

    Ha j'ai oublié le traitement concernant le repertoire, à mettre au debut de la fonction ListeRep.
    Avantage : le traitement des exceptions est un peu plus fin
    Inconvénients :
    - c'est plus long
    - si dans un sous répertoire, un des fichiers génère une exception, on n'obtient aucun fichier (même ceux ne posant pas problème)
    - si dans un répertoire, un sous-répertoire génère une exception, on n'obtient aucun sous-répertoire


    Voila, la je sèche !
    Il y a surement d'autres méthodes (et classes ...) pour faire ce que je veux :
    nous renvoyer une liste des répertoires et fichiers avec leurs infos, et passant sur ceux posant problème
    (le nec serait de nous mettre en plus ceux qui posent problème). Le tout le plus rapidement possible.

    A savoir :
    un "vieux" dir sous dos nous fait ca en 4s pour 65000 fichiers (comme le getfiles complet).
    Le seul problème étant qu'il ne gère pas les caractères non classiques (certains noms de fichiers/
    répertoires sont en japonnais, hé oui ...).


    J'espère que vous pourrez me guider vers une solution,
    Merci ...
    Damien.



    mardi 21 juillet 2009 14:51

Réponses

  • J'ai trouvé !!!!

    Alors je faisai :
    try
    
          For Each oFich As FileInfo In oRep.GetFiles()
    
              'traitement
    
          End
    
        Catch ex As Exception
    
              'traitement si echec
    
        End Try













    En faisant plus de recherches, je me suis rendu compte que l'exception etait générée lors de l'accès
    à oFich.FullName (une System.IO.PathTooLongException si le chemin est trop long).
    Hors on peut quand meme avoir accès à oFichName...
    Il ne faut donc plus utiliser for each mais for i..., je provoque quand meme une exception pour récupere
    son type ...

    D'où le code suivant :

    Public Sub ListeRep(ByRef oRep As DirectoryInfo)
       'Test : est ce que le repertoire existe ?
        If Not oRep.Exists Then
            Return
        End If
    
        'On parcours tous les fichiers du rep
    Dim cFich as FileInfo()
    Dim sCh as String
    cFich=oRep.GetFiles()
    For i as integer From 0 TO cFich.GetLength(0)-1
       Try
            sCh=cFich(i).fullname
            'puis traitement
       Catch ex As Exception
            sCh=oRep.FullName + "\" + cFich(i).Name
              'puis traitement (mais on n'a pas accès à toutes les infos du fichier (à presque rien en fait, pusqu'il n est pas trouvé!)
              'pour trouver l'exception : ex.GetType.ToString
       End Try
    
    Next
        
    
       'On parcours tous les sous repertoires (recurrence)
    dim cSousRep as DirectoryInfo()
    cSousRep=oRep.GetDirectories()
    For i as integer=0 to cSousRep.GetLength(0)-1
        'Si n'existe pas, on peut recuperer le nom complet via
        'oRep.FullName + "\" + cSousRep(i).Name
        'Mais aucune méthode ne marchera sur cSousRep(i) (tel que GetFiles au autre)
        If oSousRep(i).exists
             ListeRep(oSousRep)
        end if
    Next
              
    
    End Sub
    
    jeudi 23 juillet 2009 15:13

Toutes les réponses

  • Salut,

    J'ai fait quelque chose de similaire dans le passé et qui était tout de même performant (l'application parcourait un disque dur avec environ 150Go de données). J'avais utiliser le BackgroundWorker pour faire cela dans un Thread séparé. Ce n'était pas parfait, mais pour parcourir 150GO de données (des milliers de fichiers/répertoires) on avait un temps d'environ 5min, ce qui était acceptable dans notre cas.
    Microsoft MVP C# :: mongeon.devrpm.ca
    mardi 21 juillet 2009 17:30
    Modérateur
  • Merci,

    l'idée etait bonne mais ne convient pas vraiment :
    la procedure de parcours doit etre blocante dans notre logiciel.

    De plus ceci ne concerne que la vitesse, et finalement après concertation on peut accepter
    des temps un peu plus long (et a priori ce n est pas trop long).

    Il reste donc juste a creuser sur le deuxieme probleme : obtenir les fichiers et rep qui ne plantent pas
    dans un rep avec un ou plusieurs fichiers/rep qui plante.

    Voila ...
    mercredi 22 juillet 2009 08:50
  • J'ai trouvé !!!!

    Alors je faisai :
    try
    
          For Each oFich As FileInfo In oRep.GetFiles()
    
              'traitement
    
          End
    
        Catch ex As Exception
    
              'traitement si echec
    
        End Try













    En faisant plus de recherches, je me suis rendu compte que l'exception etait générée lors de l'accès
    à oFich.FullName (une System.IO.PathTooLongException si le chemin est trop long).
    Hors on peut quand meme avoir accès à oFichName...
    Il ne faut donc plus utiliser for each mais for i..., je provoque quand meme une exception pour récupere
    son type ...

    D'où le code suivant :

    Public Sub ListeRep(ByRef oRep As DirectoryInfo)
       'Test : est ce que le repertoire existe ?
        If Not oRep.Exists Then
            Return
        End If
    
        'On parcours tous les fichiers du rep
    Dim cFich as FileInfo()
    Dim sCh as String
    cFich=oRep.GetFiles()
    For i as integer From 0 TO cFich.GetLength(0)-1
       Try
            sCh=cFich(i).fullname
            'puis traitement
       Catch ex As Exception
            sCh=oRep.FullName + "\" + cFich(i).Name
              'puis traitement (mais on n'a pas accès à toutes les infos du fichier (à presque rien en fait, pusqu'il n est pas trouvé!)
              'pour trouver l'exception : ex.GetType.ToString
       End Try
    
    Next
        
    
       'On parcours tous les sous repertoires (recurrence)
    dim cSousRep as DirectoryInfo()
    cSousRep=oRep.GetDirectories()
    For i as integer=0 to cSousRep.GetLength(0)-1
        'Si n'existe pas, on peut recuperer le nom complet via
        'oRep.FullName + "\" + cSousRep(i).Name
        'Mais aucune méthode ne marchera sur cSousRep(i) (tel que GetFiles au autre)
        If oSousRep(i).exists
             ListeRep(oSousRep)
        end if
    Next
              
    
    End Sub
    
    jeudi 23 juillet 2009 15:13
  • Bonjour,

    Bien évidemment, il est fortement déconseillé d'attraper les exceptions de manière générique via un :

    Catch ex As Exception

    Sous peine de récolter des exceptions intraitable et permettant de faire fonctionner votre application de manière instable !
    Préférez de traiter les exceptions de manière spécifique en spécifiant les types d'exceptions à récolter :

    Catch ex as PathTooLongException
    ...

    Cordialement
    Gilles TOURREAU - MVP C#
    dimanche 26 juillet 2009 10:16
    Modérateur