php - count files in subfolder-level only, echo total per folder - php

Following folder structure:
/files/<user_id>/<filename>.txt
Examples:
`/files/15/file1.txt`
`/files/15/file2.txt`
`/files/21/file1.txt`
`/files/23/file1.txt`
I need to count the total number of files in each subfolder, but only on the subfolder level. Meaning, if there is another folder, like /files/23/dir/file1.txt, then this folder and its content should not be counted.
Output:
<folder_name>: <folder_count> files
Examples:
15: 23 files
21: 2 files
23: 5 files
How can one do a recursive count for subdirectories, but ignore directories in the subdirectory?
Thanks
Edit:
My code so far:
<?php
// integer starts at 0 before counting
$i = 0;
$path = '../../../../../../../home/bpn_sftp';
$dirs = glob($path . '/*' , GLOB_ONLYDIR);
foreach($dirs as $dir){
while (($file = readdir($dir)) !== false){
if (!in_array($file, array('.', '..')) && !is_dir($dir.$file))
{
$file_count = count( glob($dir.'*.txt') );
echo $dir." has ".$file_count." files<br>";
$i++;
}
}
}
echo "Total count: ".$i." files";
?>

Managed to make it work with a recursive folder scan, limiting the file count to the filetype I am looking for.
<?php
// integer starts at 0 before counting
$i = 0;
$path = './test';
$dirs = glob($path . '/*' , GLOB_ONLYDIR);
foreach($dirs as $dir){
$file_count = count( glob($dir.'/*.txt') );
echo $dir." has ".$file_count." files<br>";
$i++;
}
echo "Total count: ".$i." files";
?>

Related

how to check if folder contains only files with php

I want to check if a folder contains at least 1 real file. I tried this code:
$dir = "/dir/you/want/to/scan";
$handle = opendir($dir);
$folders = 0;
$files = 0;
while(false !== ($filename = readdir($handle))){
if(($filename != '.') && ($filename != '..')){
if(is_dir($filename)){
$folders++;
} else {
$files++;
}
}
}
echo 'Number of folders: '.$folders;
echo '<br />';
echo 'Number of files: '.$files;
when in folder scan are 1 subfolder and 2 real files; the code above gives me as output:
Number of folders: 0
Number of files: 3
So it seems that a subfolder is seen as a file. But i want only real files to be checked. How can i achieve that?
You can do this job easily using glob():
$dir = "/dir/you/want/to/scan";
$folders = glob($dir . '/*', GLOB_ONLYDIR);
$files = array_filter(glob($dir . '/*'), 'is_file');
echo 'Number of folders: ' . count($folders);
echo '<br />';
echo 'Number of files: ' . count($files);
based on your first line, where you specify a path, which is different to your scripts path, you should combine $dir and the $filename in the is_dir if-clause.
Why?
Because if your script is on:
/var/web/www/script.php
and you check the $dir:
/etc/httpd
which contains the subfolder "conf", your script will check for the subfolder /var/web/www/conf
You can use scandir
scandir — List files and directories inside the specified path
<?php
$dir = "../test";
$handle = scandir($dir);
$folders = 0;
$files = 0;
foreach($handle as $filename)
{
if(($filename != '.') && ($filename != '..'))
{
if(is_dir($filename))
{
$folders++;
}
else
{
$files++;
}
}
}
echo 'Number of folders: '.$folders;
echo '<br />';
echo 'Number of files: '.$files;
?>

How to count number of files in folder in php?

I want to enable users to upload some files (pictures) in their own folders. But that should be possible only if that folders contain less than five pictures. If there are 5 pictures already, script has to let know user that his/her folder is full.
So, I wonder if there is function in php that count number of files in folder. Or any other way in php to do that? Thanks in advance.
Use the FilesystemIterator as shown:
$dir = "/path/to/folder";
$fi = new FilesystemIterator($dir, FilesystemIterator::SKIP_DOTS);
$fileCount = iterator_count($fi);
Nothing easier: use opendir() and readdir() just like follow:
<?php
$images_extension_array = array("jpg","jpeg","gif","png");
$dir = "/path/to/user/folder";
$dir_resource = opendir($dir);
$file_count = 0;
while (($file = readdir($dir_resource)) !== false) { // scan directory
$extension_from = strrpos($file,"."); // isolate extension index/offset
if ($extension_from && in_array(substr($file,$extension_from+1), $images_extension_array))
$file_count ++; //if has extension and that extension is "associated" with an image, count
}
if ($number_of_files == %) {
//do stuff
}
Obviously this doesn't take into account file extensions...
You can also use:
scandir() ---> read here
FilesystemIterator class (as dops's answer correctly suggest) ---> read here
You could just let PHP find the files for you...then count them.
$count = count(glob("$path_to_user_dir/*"));
I really like dops answer, but it will return the count of files, directories, and symlinks, which may not be the goal. If you just want a count of the local files in a directory, you can use:
$path = "/path/to/folder";
$fs = new FilesystemIterator($path);
foreach($fs as $file) {
$file->isFile() ? ++$filecount : $filecount;
}
This little function here is a modification to some code I found a little while ago that will also count all of the sub Folders and everything in those folders as well:
<?PHP
$folderCount = $fileCount = 0;
countStuff('.', $fileCount, $folderCount);
function countStuff($handle, &$fileCount, &$folderCount)
{
if ($handle = opendir($handle)) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
if (is_dir($entry)) {
echo "Folder => " . $entry . "<br>";
countStuff($entry, $fileCount, $folderCount);
$folderCount++;
} else {
echo "File => " . $entry . "<br>";
$fileCount++;
}
}
}
closedir($handle);
}
}
echo "<br>==============<br>";
echo "Total Folder Count : " . $folderCount . "<br>";
echo "Total File Count : " . $fileCount;
?>
NOTE: I will also post the original code that will just count the files and folders of the parent directory and not the sub-folders children below:
if ($handle = opendir('.')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
if (is_dir($entry)) {
echo "Folder => " . $entry . "<br>";
countStuff($entry, $fileCount, $folderCount);
$folderCount++;
} else {
echo "File => " . $entry . "<br>";
$fileCount++;
}
}
}
echo "<br>==============<br>";
echo "Total Folder Count : " . $folderCount . "<br>";
echo "Total File Count : " . $fileCount;
closedir($handle);
}
You can use
$nbFiles=count(scandir('myDirectory'))-2;
(-2 is for removing "." and "..")

DirectoryIterator still showing 3 files when the forder is empty

I have following code to count number of files in a folder using php
$x=0;
$filepath=$uplaod_drive."Workspace/12345";
$dir = new DirectoryIterator($filepath);
foreach($dir as $file ){
$x++;
}
But even if the folder is empty it show 3 files are there and if the folder has 8 files it show 11 files.
I would be very thankful if someone could explain this ..Thanks.
if you want to count only regular files:
$x=0;
$filepath=$uplaod_drive."Workspace/12345";
$dir = new DirectoryIterator($filepath);
foreach($dir as $file ){
if ($file->isFile()) $x++;
}
or if you want to skip the directories:
$x=0;
$filepath=$uplaod_drive."Workspace/12345";
$dir = new DirectoryIterator($filepath);
foreach($dir as $file ){
if (!$file->isDir()) $x++;
}
or if you want to skip the dot files:
$x=0;
$filepath=$uplaod_drive."Workspace/12345";
$dir = new DirectoryIterator($filepath);
foreach($dir as $file ){
if (!$file->isDot()) $x++;
}
The DirectoryIterator is counting the current directory (denoted by '.') and the previous directory (denoted by '..') as $files. Debug your code like so.
$x=0;
$filepath='C:\xampp\htdocs\\';
$dir = new DirectoryIterator($filepath);
foreach($dir as $file ){
echo $file . "<br/>";
$x++;
}
echo $x;
Then as mentioned in the comment above by #Prix you can skip if $file->isDot(), and if you dont want to count the directories then also skip if not $file->isFile().
$x=0;
$filepath='C:\xampp\htdocs\\';
$dir = new DirectoryIterator($filepath);
foreach($dir as $file ){
if ($file->isDot() || !$file->isFile()) continue;
//echo $file . "<br/>";
$x++;
}
echo $x;

Load Random Images from Directory

I'd like to randomly load images from a directory and have a button somewhere that refreshes the entire page. Here's the current code I have now:
<?php
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
foreach ($a as $i) {
echo "<img src='" . $dir . '/' . $i . "' />";
}
?>
The problem is it loads all 400,000 images at once. I only want 30 to load. 30 random images from the directory. I tried looking up some code such as modifying the above to this:
<?php
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
foreach ($a as $i) {
echo "<img src='" . $dir . '/' . $i . "' />";
if (++$i == 2) break;
}
?>
But it seems to do absolutely nothing.. So if someone can help me get 30 random photos from that directory to load and have some type of reload button, that would be of great help.
Thank you in advance
Here is my solution with a cache:
<?php
define('CACHE_FILE', 'mycache.tmp');
define('CACHE_TIME', 20); // 20 seconds (for testing!)
define('IMG_COUNT', 30);
define('IMG_DIR', '../public/wp-content/uploads/2012/01');
/**
* Loads the list (an array) from the cache
* Returns FALSE if the file couldn't be opened or the cache was expired, otherwise the list (as an array) will be returned.
*/
function LoadListFromCache($cacheFile, $cacheTime)
{
if ( file_exists($cacheFile) )
{
$fileHandle = fopen($cacheFile, 'r');
if ( !$fileHandle )
return false;
// Read timestamp (separated by "\n" from the content)
$timestamp = intval( fgets($fileHandle) );
fclose($fileHandle);
// Expired?
if ( $timestamp+$cacheTime > time() )
return false;
else
{
// Unserialize the content!
$content = file_get_contents($cacheFile);
$content = substr( $content, strpos($content, "\n") );
$list = unserialize($content);
return $list;
}
}
return false;
}
/**
* Caches the passed array
* Returns FALSE if the file couldn't be opened, otherwise TRUE.
*/
function SaveListToCache($cacheFile, $list)
{
$fileHandle = fopen($cacheFile, 'w');
if ( $fileHandle === FALSE ) return false;
fwrite($fileHandle, time());
fwrite($fileHandle, "\n");
fwrite($fileHandle, serialize($list));
fclose($fileHandle);
return true;
}
/**
* Generates the list of all image files (png, jpg, jpeg) and caches it.
* Returns the list as an array.
*/
function GenerateList()
{
$a = array();
$dir = IMG_DIR;
if ($handle = opendir($dir))
{
while (false !== ($file = readdir($handle)))
{
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
SaveListToCache(CACHE_FILE, $a);
return $a;
}
function GetRandomImages($list, $count)
{
$listCount = count($list);
$randomEntries = array();
for ($i=0; $i<$count; $i++)
{
$randomEntries[] = $list[ rand(0, $listCount) ];
}
return $randomEntries;
}
// This code will execute the other functions!
$list = LoadListFromCache(CACHE_FILE, CACHE_TIME);
if ( $list === FALSE )
{
$list = GenerateList();
}
$images = GetRandomImages($list, IMG_COUNT);
foreach ($images as $image)
{
echo '<img src="', IMG_DIR.DIRECTORY_SEPARATOR.$image, '" />';
}
If you have 400,000 images then I think reading the entire directory everytime is going to be an expensive means of showing random images. I would use a database instead and store the file paths in it.
If you want to use your existing code then think of it this way. You have an array of length n containing image names. You want to generate thirty random numbers between 0 and n-1. Then display the image associated with that position in the array. I'm not a php expert, but here is some pseudocode:
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
for ( i=0; i < 30; i++) {
//generate a random number between 0 and N-1
random = rand(0, $a.length - 1);
//display that image in the array
echo "<img src='" . $dir . '/' . $a[random] . "' />";
}
You need to create a new variable for the counter instead of using $i
For example, you can do this instead
$j = 0;
foreach ($a as $i) {
echo "<img src='" . $dir . '/' . $i . "' />";
$j++;
if ($j >= 30)
{
break;
}
}
EDIT: Perhaps for the random part you can first generate a random number between 0 and n-1 where n is the total number of the images and then just echo out the image from the array with the index number.
Instead of using foreach, I think you'll need a for loop instead.
$totalImgs = count($a);
$imgUsed = array();
for ($j = 0; $j < 30; $j++)
{
do
{
$randIndex = mt_rand(0, $totalImgs);
}
while ($imgUsed[$randIndex] === TRUE);
$imgUsed[$randIndex] = TRUE;
echo "<img src='" . $dir . '/' . $a[$randIndex] . "' />";
}
You should maybe only read 30 file from your directory. Stop looking in the directory when readdir return false or your array's length is 30.
This should work
$a = array();
$dir = '../public/wp-content/uploads/2012/01';
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle)) && (count($a) <= 30) {
if (preg_match("/\.png$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpg$/", $file)) $a[] = $file;
elseif (preg_match("/\.jpeg$/", $file)) $a[] = $file;
}
closedir($handle);
}
It may not execute (I didn't try). But the idea is here
For randomize the image: shuffle($a) should do the trick
in simplest way ,
you can use
find , sort , head
commands in linux,in conjuction with PHP's built in
exec()
function to get 30 random image links easily , the folowing snippet lists how to do it
(How to get random 30 image links in an array.)
<?php
$picdir = "directory/containing/pictures"; // directory containing only pictures
exec("find " . $picdir . " | sort -R | head -30 ",$links);
while(list($index,$val) = each($links) ) {
echo "<img src =" .$val . "> <br/>"; // shows image
}
?>
Here $links array contain random 30 image names(from folder) with complete path . This is used with img tag in echo to generate images
Here $picdir has the path of the directory having images and it is assumed that dirrectory is having only image files . in other case its only matter of modifying find command to exclude non image files(such as using grep command to exclude )

Rename all files in order in PHP

I have the following files:
1.jpg
2.jpg
3.jpg
4.jpg
When I remove 2.jpg, I want 3.jpg to become 2.jpg and 4.jpg to become 3.jpg
I tried a for loop with the rename function, but it does not seem to work:
for($a = $i;$a < $filecount;$a ++)
{
rename('photo/'.($a+1).'.jpg', 'photo/'. ($a).'.jpg');
}
Where $i is the number of the photo I just deleted.
List all files, sorted by name:
$files = glob('../photos/*');
Foreach file, rename it if necessary:
foreach($files as $i => $name) {
$newname = sprintf('../photos/%d.jpg', $i+1);
if ($newname != $name) {
rename($name, $newname);
}
}
Why don't you just remove the file you don't longer need and than rename all files that remain in the folder, starting with one. Like:
<?php
$fileToRemove= '3.jpg';
unlink($fileToRemove);
$cnt = 0;
if ($handle = opendir('.')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
rename($file, ++$cnt+".jpg");
}
}
closedir($handle);
}
?>
This way, you are always sure that the sequence is correct. If you have a lot of files, you can of cource start renaming from the number of the file you are deleting.
I would have do it this way:
echo "<pre>";
$files = array('file1.jpg', 'file5.jpg', 'file7.jpg', 'file9.jpg');
function removeElement($array, $id){
$clone = $array; // we clone the array for naming usage
$return = array(); // the array returned for testing purposes
$j = 0;
foreach($array as $num => $file){ // loop in the elements
if($file != $id){ // check if the current file is not the element we want to remove
if($num == $j){ // if current element and '$j' are the same we do not need to rename that file
$return[] = "// #rename('".$file."', '".$file."'); -- do not rename";
} else { // if previously we have removed the file '$id' then '$j' should be -1 and we rename the file with the next one stored in '$clone' array
$return[] = "#rename('".$file."', '".$clone[($num-1)]."'); // rename";
}
} else { // this is for the file we need to remove, we also -1 current '$j'
$j--;
}
$j++;
}
return $return;
}
print_r(removeElement($files, 'file5.jpg'));
it seems rudimentary, but it works and is easy to read.
$filecount = 5;
$i = 2;
unlink('photo/'. $i . '.jpg');
for($i; $i < $filecount; $i++) {
rename('photo/'. ($i+1) .'.jpg', 'photo/'. $i . '.jpg');
}
die;

Categories