8.5.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.0.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.1.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.2.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.3.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.4.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.6.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
4.6.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
4.7.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
5.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
6.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
7.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)

Finds all files that match a given mask in a given directory.

Directories and files beginning with a period are excluded; this prevents hidden files and directories (such as SVN working directories) from being scanned.

Parameters

$dir: The base directory for the scan, without trailing slash.

$mask: The regular expression of the files to find.

$nomask: An array of files/directories to ignore.

$callback: The callback function to call for each match.

$recurse: When TRUE, the directory scan will recurse the entire tree starting at the provided directory.

$key: The key to be used for the returned associative array of files. Possible values are "filename", for the path starting with $dir; "basename", for the basename of the file; and "name" for the name of the file without the extension.

$min_depth: Minimum depth of directories to return files from.

$depth: Current depth of recursion. This parameter is only used internally and should not be passed in.

Return value

An associative array (keyed on the provided key) of objects with "filename", "basename", and "name" members corresponding to the matching files.

Related topics

8 calls to file_scan_directory()
drupal_clear_css_cache in includes/common.inc
Delete all cached CSS files.
drupal_clear_js_cache in includes/common.inc
Delete all cached JS files.
drupal_system_listing in includes/common.inc
Return an array of system file objects.
image_get_available_toolkits in includes/image.inc
Return a list of available toolkits.
install_find_locales in ./install.php
Find all .po files for the current profile.

... See full list

File

includes/file.inc, line 1074
API for handling file uploads and server file management.

Code

function file_scan_directory($dir, $mask, $nomask = array(
  '.',
  '..',
  'CVS',
), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0) {
  $key = in_array($key, array(
    'filename',
    'basename',
    'name',
  )) ? $key : 'filename';
  $files = array();
  if (is_dir($dir) && ($handle = opendir($dir))) {
    while (FALSE !== ($file = readdir($handle))) {
      if (!in_array($file, $nomask) && $file[0] != '.') {
        if (is_dir("{$dir}/{$file}") && $recurse) {

          // Give priority to files in this folder by merging them in after any subdirectory files.
          $files = array_merge(file_scan_directory("{$dir}/{$file}", $mask, $nomask, $callback, $recurse, $key, $min_depth, $depth + 1), $files);
        }
        elseif ($depth >= $min_depth && @ereg($mask, $file)) {

          // Always use this match over anything already set in $files with the same $$key.
          $filename = "{$dir}/{$file}";
          $basename = basename($file);
          $name = substr($basename, 0, strrpos($basename, '.'));
          $files[${$key}] = new stdClass();
          $files[${$key}]->filename = $filename;
          $files[${$key}]->basename = $basename;
          $files[${$key}]->name = $name;
          if ($callback) {
            $callback($filename);
          }
        }
      }
    }
    closedir($handle);
  }
  return $files;
}

Comments

zorroposada’s picture

I think its very useful to show the structure of the array that results from this function.

Array
(
    [path/to/file.txt] => stdClass Object
        (
            [filename] => path/to/file.txt
            [basename] => file.txt
            [name] => file
        )

)
rschwab’s picture

The array is ordered by timestamp, not filename. So if you step through the results, the oldest file will be the first.

joelstein’s picture

@rschwab: Actually, since file_scan_directory() uses readdir(), the files will be returned in the order in which they are stored on the filesystem (according to the PHP documentation). So, it depends on your file system. The only way to guarantee the sort order of your files is to use some sort functions, like ksort or krsort.

andreiashu’s picture

Its worth noting that the $mask attribute is case sensitive.

jmcoder’s picture

also note that $mask is old, ereg regular expression

in 7 it's pcre

sime’s picture

This example shows how to find all files of a certain type (in this case .inc) in a specific module.

$directory = drupal_get_path('module', 'mymodule') . '/subfolder';
$mask = '.inc';
$files = file_scan_directory($directory, $mask);
drupalfever’s picture

The function file_scan_directory has a little bug.

When you use it to scan a folder for images, for example, it returns a list of files with one extra forward slash as you can see on the following example:



$files = file_scan_directory('files', '.jpg');

foreach ($files as $value) {
  
  echo $value->filename . '
'; }

The above example will print the following:

sites/drupal/files//image1.jpg
sites/drupal/files//image2.jpg
sites/drupal/files//image3.jpg
sites/drupal/files//image4.jpg
etc...

Notice that the file names are preceded by two forward slashes.

scubaguy’s picture

It took me a bit to sort this out so maybe my experience can save someone else a little time.

The regex can not be the full regex string as you see in a regex builder. For example to find all files with extensions that case insensitive match .jpg, .jpeg, gif, png you would use:

file_scan_directory($path, '.[Jj][Pp][Ee]?[Gg]|.[Pp][Nn][Gg]|[Gg][Ii][Ff]')

I used this to loop through folders of uploaded images and then if images were found to create a node with a cck image field including all the images found in each folder.

      $queryClassrooms =  db_query('SELECT nid FROM {node} WHERE type = "classroom"');

      while ($classrooms = db_fetch_object($queryClassrooms)) {

        $classroom_node = node_load($classrooms->nid);

        // this will have to be tweaked for your site's taxonomy.  I had to set it manually for each one. There is probably a better way to do this.
        $taxonomy = strtolower($classroom_node->taxonomy[10]->name);
        if ($taxonomy == null) $taxonomy = $classroom_node->taxonomy[11]->name;

        $classroom_title = $classroom_node->title;
        $classroom = make_safe($classroom_node->title);
        $taxonomy = make_safe($taxonomy);

        // directory to scan for new image files and if it doesn't exist create it
        $path = 'sites/default/files/gallery/classrooms/'.$taxonomy.'/'.$classroom.'/uploaded';
        if (!is_dir($path)) mkdir($path, 777, true);

        // directory to moved processed files into and if it doesn't exist create it
        $processed = 'sites/default/files/gallery/classrooms/'.$taxonomy.'/'.$classroom.'/processed';
        if (!is_dir($processed)) mkdir($processed, 777, true);

        // find all matching image files in the directory
        $files = file_scan_directory($path, '.[Jj][Pp][Ee]?[Gg]|.[Pp][Nn][Gg]|[Gg][Ii][Ff]');

        if ($files) {
                // If files are found create a new node
	        $node = new stdClass();
	        $node->type = 'gallery';
	        $node->title = $classroom_title.' Photos - '.date('M j, Y');
	        $node->uid = 1;
	        $node->status = 1;
	        $node->is_new = TRUE;

			foreach ($files as $file_data) {
	     		$filepath = $file_data->filename;
				if (file_exists($filepath)) {
                                        // shrink the file down to a more web friendly size if storage space is an issue for you
                                        exec("convert \"$filepath\" -resize 1024x1024 \"$filepath\"; mv \"$filepath\" \"$filepath\"");
                                        // process the file into Drupal
					$field = field_file_save_file($filepath, array(), $processed, FILE_EXISTS_REPLACE);
					$node->field_images[] = $field;
                                        // delete the original file after it has been processed and moved
					unlink($filepath);
				}
			}
      		$node = node_submit($node);
      		node_save($node);
Wolfgaar’s picture

I am still relatively new to managing a drupal site, so I don't have a lot of experience with it. I have one to two questions about trying to scan/sweep for possible malicious files and keep coming back to this point: How does one use this that is still relatively new to drupal? I am trying to sweep for any spam code and things like that on my server. I am currently still using drupal 6.

Any assistance and pointers would be appreciated.
Thank you for your time.