Search code examples
vb.netiodirectorygetfiles

Limiting the amount of files grabbed from system.io.directory.getfiles


I've got a folder browser dialogue populating the directory location (path) of a system.io.directory.getfiles. The issue is if you accidentally select a folder with hundereds or thousands of files (which there's no reason you would ever need this for this app) it will lock up the app while it grabs all the files. All I'm grabbing are the directory locations as strings and want to put a limit on the amount of files that can be grabbed. Here's my current code that isn't working.

 If JigFolderBrowse.ShowDialog = DialogResult.OK Then
            Dim dirs(50) As String
            dirs = System.IO.Directory.GetFiles(JigFolderBrowse.SelectedPath.ToString, "*", System.IO.SearchOption.AllDirectories)
            If dirs.Length> 50 Then
                MsgBox("Too Many Files Selected" + vbNewLine + "Select A Smaller Folder To Be Organized")
                Exit Sub
            End If
            'Seperate Each File By Type
            For i = 0 To dirs.Length - 1
                    If Not dirs(i).Contains("~$") Then
                        If dirs(i).Contains(".SLDPRT") Or dirs(i).Contains(".sldprt") Then
                            PartsListBx.Items.Add(dirs(i))
                        ElseIf dirs(i).Contains(".SLDASM") Or dirs(i).Contains(".sldasm") Then
                            AssemListBx.Items.Add(dirs(i))
                        ElseIf dirs(i).Contains(".SLDDRW") Or dirs(i).Contains(".slddrw") Then
                            DrawingListBx.Items.Add(dirs(i))
                        ElseIf dirs(i).Contains(".pdf") Or dirs(i).Contains(".PDF") Then
                            PDFsListBx.Items.Add(dirs(i))
                        ElseIf dirs(i).Contains(".DXF") Or dirs(i).Contains(".dxf") Then
                            DXFsListBx.Items.Add(dirs(i))
                        ElseIf Not dirs(i).Contains(".db") Then
                            OtherFilesListBx.Items.Add(dirs(i))
                        End If
                    End If

Solution

  • The Directory.GetFiles method always retrieves the full list of matching files before returning. There is no way to limit it (outside of specifying a more narrow search pattern, that is). There is, however, the Directory.EnumerateFiles method which does what you need. From the MSDN article:

    The EnumerateFiles and GetFiles methods differ as follows: When you use EnumerateFiles, you can start enumerating the collection of names before the whole collection is returned; when you use GetFiles, you must wait for the whole array of names to be returned before you can access the array. Therefore, when you are working with many files and directories, EnumerateFiles can be more efficient.

    So, for instance, you could do something like this:

    dirs = Directory.
        EnumerateFiles(
            JigFolderBrowse.SelectedPath.ToString(), 
            "*", 
            SearchOption.AllDirectories).
        Take(50).
        ToArray()
    

    Take is a LINQ extension method which returns only the first x-number of items from any IEnumerable(Of T) list. So, in order for that line to work, you'll need to import the System.Linq namespace. If you can't, or don't want to, use LINQ, you can just implement your own method that does the same sort of thing (iterates an IEnumerable list in a for loop and returns after reading only the first 50 items).

    Side Note 1: Unused Array

    Also, it's worth mentioning, in your code, you initialize your dirs variable to point to a 50-element string array. You then, in the very next line, set it to point to a whole new array (the one returned by the Directory.GetFiles method). While it's not breaking functionality, it is unnecessarily inefficient. You're creating that extra array, just giving the garbage collector extra work to do, for no reason. You never use that first array. It just gets dereferenced and discarded in the very next line. It would be better to create the array variable as null:

    Dim dirs() As String
    

    Or

    Dim dirs() As String = Nothing
    

    Or, better yet:

    Dim dirs() As String = Directory.
        EnumerateFiles(
            JigFolderBrowse.SelectedPath.ToString(), 
            "*", 
            SearchOption.AllDirectories).
        Take(50).
        ToArray()
    

    Side Note 2: File Extension Comparisons

    Also, it looks like you are trying to compare the file extensions in a case-insensitive way. There are two problems with the way you are doing it. First, you only comparing it against two values: all lowercase (e.g. ".pdf") and all uppercase (e.g. ".PDF). That won't work with mixed-case (e.g. ".Pdf").

    It is admittedly annoying that the String.Contains method does not have a case-sensitivity option. So, while it's a little hokey, the best option would be to make use of the String.IndexOf method, which does have a case-insensitive option:

    If dirs(i).IndexOf(".pdf", StringComparison.CurrentCultureIgnoreCase) <> -1 Then
    

    However, the second problem, which invalidates my last point of advice, is that you are checking to see if the string contains the particular file extension rather than checking to see if it ends with it. So, for instance, a file name like "My.pdf.zip" will still match, even though it's extension is ".zip" rather than ".pdf". Perhaps this was your intent, but, if not, I would recommend using the Path.GetExtension method to get the actual extension of the file name and then compare that. For instance:

    Dim ext As String = Path.GetExtension(dirs(i))
    If ext.Equals("pdf", StringComparison.CurrentCultureIgnoreCase) Then
        ' ...