Get highest numbered file and create next one - php

I have a folder that contains files named standard_xx.jpg (xx being a number)
I would like to find the highest number so that I can get the filename ready to rename the next file being uploaded.
Eg. if the highest number is standard_12.jpg
$newfilename = standard_13.jpg
I have created a method to do it by just exploding the file name but it isn't very elegant
$files = glob($uploaddir.'test-xrays-del/standard_*.JPG');
$maxfile = $files[count($files)-1];
$explode = explode('_',$maxfile);
$filename = $explode[1];
$explode2 = explode('.',$filename);
$number = $explode2[0];
$newnumber = $number + 1;
$standard = 'test-xrays-del/standard_'.$newnumber.'.JPG';
echo $newfile;
Is there a more efficient or elegant way of doing this?

I'd do it like this myself:
<?php
$files = glob($uploaddir.'test-xrays-del/standard_*.JPG');
natsort($files);
preg_match('!standard_(\d+)!', end($files), $matches);
$newfile = 'standard_' . ($matches[1] + 1) . '.JPG';
echo $newfile;

You can make use of sscanfDocs:
$success = sscanf($maxfile, 'standard_%d.JPG', $number);
It will allow you to not only pick out the number (and only the number) but also whether or not this worked ($success).
Additionally you could also take a look into natsortDocs to actually sort the array you get back for the highest natural number.
A complete code-example making use of these:
$mask = 'standard_%s.JPG';
$prefix = 'test-xrays-del';
$glob = sprintf("%s%s/%s", $uploaddir, $prefix, sprintf($mask, '*'));
$files = glob($glob);
if (!$files) {
throw new RuntimeException('No files found or error with ' . $glob);
}
natsort($files);
$maxfile = end($files);
$success = sscanf($maxfile, sprintf($mask, '%d'), $number);
if (!$success) {
throw new RuntimeException('Unable to obtain number from: ', $maxfile);
}
$newnumber = $number + 1;
$newfile = sprintf("%s/%s", $prefix, sprintf($mask, $newnumber));

Try with:
$files = glob($uploaddir.'test-xrays-del/standard_*.JPG');
natsort($files);
$highest = array_pop($files);
Then get it's number with regex and increase value.

Something like this:
function getMaxFileID($path) {
$files = new DirectoryIterator($path);
$filtered = new RegexIterator($files, '/^.+\.jpg$/i');
$maxFileID = 0;
foreach ($filtered as $fileInfo) {
$thisFileID = (int)preg_replace('/.*?_/',$fileInfo->getFilename());
if($thisFileID > $maxFileID) { $maxFileID = $thisFileID;}
}
return $maxFileID;
}

Related

Glob no longer working with Nginx

Edit
There is nothing wrong with my code so feel free to use it as it is, if ever you need to search your file system from within PHP then echo the results.
I created a class to search through files using glob. It worked perfectly but now that I have migrated from Apache to Nginx, it always returns 0 results.
Here is my code:
public static function search($path, $find, $caseSensitive = false)
{
if ($path[strlen($path) - 1] !== '/')
$path .= '/';
$path = '../'.$path;
$pathLen = strlen($path);
$path .= '*';
if ($caseSensitive)
$files = self::globRecursive($path.$find);
else
{
$findLen = strlen($find);
for ($i = 0; $i < $findLen; $i++)
$find1 .= '['.strtolower($find[$i]).strtoupper($find[$i]).']';
$files = self::globRecursive($path.$find1);
}
$message = '';
$count = count($files);
if ($count === 0)
return '"'.$find.'" not found.';
foreach ($files as $file)
$message .= substr($file, $pathLen).'<br />';
return '"'.$find.'" found in '.$count.' files:<br />'.$message;
}
private static function globRecursive($pattern, $flags = 0)
{
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
$files = array_merge($files, self::globRecursive($dir.'/'.basename($pattern), $flags));
return $files;
}
The problem was due to changing my root directy. Simply prepending ../ to my search query fixed the problem.

Rename Directory using PHP Script

I have a directory containing folders like
baseurl/2-435435435_323423/
baseurl/5_435435435_32423/
baseurl/3_543543543_2342342/
Now i want to rename all the folders from their original name to new name i.e. truncate last part separated by '_'. new names would be
baseurl/2-435435435/
baseurl/5_435435435/
baseurl/3_543543543/
$path_from = 'E:/documents/';
if(file_exists($path_from)){
$files = scandir($path_from);
foreach($files as $key1 => $file) {
$newName = ? // I need this
rename($path_from.$file,$path_from.$newName);
}
}
Or Let me know if there is anyway in windows to rename batch without any script.
As you mention for getting only $newName, Just use substr and strrpos.
strrpos - Find the numeric position of the last occurrence of a string
$str = 'baseurl/3_543543543_2342342/';
$pos = strrpos($str, "_");
if ($pos === false){
//do nothing
}else
$str = substr($str, 0, $pos)."/";
echo $newName = $str; //baseurl/3_543543543/
You can make use of strstr
$path_from = 'E:/documents/';
if(file_exists($path_from)){
$files = scandir($path_from);
foreach($files as $key1 => $file) {
$newName = strstr($file, '_', true);
rename($path_from.$file,$path_from.$newName);
}
}
e.g.
$str = 'baseurl/2-435435435_323423/';
$imagePreFix = strstr($str, '_', true);
echo $imagePreFix;
OUTPUT:
baseurl/2-435435435
$newName = substr($file, 0, strrpos($file, '_'));

php multiple search values in file path

I am working on a php site that needs to search a set of files with any combination of search fields.
The possible search fields are
id, year, building, lastname, firstname, birthdate
The folder structure and file names are as such
/year/building/file.pdf
The filenames contain the data to search
id_lastname_firstname_MM_dd_yy.pdf
I have everything working on the site except this part. Originally I only had ID, year, and building and I was able to do if's to check for every possibility of combinations. Now there is way more combinations so it much more complex.
I was thinking nested if and in_array or such, but there has to be a better way. I just learning my way around php.
I would like to be able to search with any combination of fields. I can change the filenames if it helps.
I started with something like this
function search($transcripts, $studentid=null, $year=null, $building=null, $last=null, $first=null, $birthdate=null){
$ext = '.pdf';
date_default_timezone_set('America/Los_Angeles');
$dir_iterator = new RecursiveDirectoryIterator("../transcripts");
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file) {
if ($file->isFile()){
$path = explode('\\',$file->getPath());
$fname = explode('_', $file->getBasename($ext));
if($path[1] == $year){
if($path[2] == $building){
if(in_array($last, $fname, true)){
if((in_array($first, $fname, true)){
if((in_array($birthdate
Originally I had seperate functions depending on which fields where filed in.
function bldStuSearch($building, $studentid, $transcripts){
$ext = '.pdf';
date_default_timezone_set('America/Los_Angeles');
$dir_iterator = new RecursiveDirectoryIterator("../transcripts");
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $file) {
$results = explode('\\',$file->getPath());
//var_dump($results);
if (($file->isFile()) && ($file->getBasename($ext)==$studentid) && ($results[2] == $building)){
//echo substr($file->getPathname(), 27) . ": " . $file->getSize() . " B; modified " . date("Y-m-d", $file->getMTime()) . "\n";
$results = explode('\\',$file->getPath());
//var_dump($results);
//$building = $results[2];
$year = $results[1];
//$studentid = $file->getBasename($ext);
array_push($transcripts, array($year, $building, $studentid));
//var_dump($transcripts);
//$size += $file->getSize();
//echo '<br>';
}
}
//echo "\nTotal file size: ", $size, " bytes\n";
if (empty($transcripts))
{
header('Location: index.php?error=2'); exit();
}
return $transcripts;
}
Now I am trying to have one search function to check for any combination? Any idea that would at least put in the right direction?
Thanks.
So I had an idea about doing a scoring system but then dismissed it. I came back to it and found a way to make it work using a weighted scoring system.
This allows the search to be super flexible and maintain being portable, not requiring a database for the metadata and using the filename as the search data without having to search each PDF. I am using A-Pdf splitter to split the PDF into separate files and add the metadata to the filename.
I hope someone some day finds this useful for other searches. I am really happy the way this turned out.
I will post the entire code when I am done on http://github.com/friedcircuits
One thing I should change is to use named keys for the arrays.
Here is the resulting code. Right now the birthdate has to be entered as m-d-yyyy to match.
function search($transcripts, $studentid=null, $year=null, $building=null, $last=null, $first=null, $birthdate=null){
$ext = '.pdf';
$bldSearch = false;
date_default_timezone_set('America/Los_Angeles');
if (($building == null) AND ($year == null)){ $searchLocation = "../transcripts";}
elseif (($year != null) AND ($building != null)){$searchLocation = "../transcripts/".$year."/".$building;}
elseif ($year != null) {$searchLocation = "../transcripts/".$year;}
elseif ($building != null) {
$searchLocation = "../transcripts/";
$bldSearch = true;
}
else{$searchLocation = "../transcripts";}
$dir_iterator = new RecursiveDirectoryIterator($searchLocation);
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
$score = 0;
foreach ($iterator as $file) {
if ($file->isFile()){
//Fix for slashes changing direction depending on search path
$path = str_replace('/','\\', $file->getPath());
$path = explode('\\',$path);
$fname = explode('_', $file->getBasename($ext));
//var_dump($path);
//echo "<br>";
//var_dump($fname);
//echo "<br>";
//fix for different search paths
if($path[1] == "transcripts"){
$pYear = $path[2];
$pbuilding = $path[3];
}
else{
$pYear = $path[1];
$pbuilding = $path[2];
}
if ($bldSearch == true){
if ($building != $pbuilding) {continue;}
}
//$fname[1] = #strtolower($fname[1]);
//$fname[2] = #strtolower($fname[2]);
if($fname[0] == $studentid){
$yearS = $pYear;
$buildingS = $pbuilding;
$studentidS = $fname[0];
$lastS = $fname[1];
$firstS = $fname[2];
$birthdateS = $fname[3];
array_push($transcripts, array($yearS, $buildingS, $studentidS, $lastS, $firstS, $birthdateS));
continue;
}
if($pYear == $year){
$score += 1;
}
if($path[2] == $building){
$score += 1;
}
if(#strpos(#strtolower($fname[1]),$last) !== false){
$score += 3;
}
if(#strpos(strtolower($fname[2]), $first) !== false){
$score += 3;
}
if($fname[3] == $birthdate){
$score += 3;
}
//echo $score." ";
if ($score > 2) {
$yearS = $pYear;
$buildingS = $pbuilding;
$studentidS = $fname[0];
$lastS = $fname[1];
$firstS = $fname[2];
$birthdateS = $fname[3];
array_push($transcripts, array($yearS, $buildingS, $studentidS, $lastS, $firstS, $birthdateS));
//var_dump($transcripts);
}
}
$score = 0;
}
if (empty($transcripts))
{
header('Location: index.php?error=2'); exit();
}
return $transcripts;}

Renaming images by number in php

I am trying to create an option of uploading an avatar to my site. What I am trying to achieve is this :
first avatar : 1.jpg
second avatar : 2.jpg
third avatar : 3.png
and so on..
How can I create an upload counter in php? My current code is this :
if(!empty($_FILES['cover']['tmp_name']))
{
$uploadfolder = "avatar/";
$file1 = rands().'.'.end(explode('.',$_FILES['cover']['name']));
$cover = $uploadfolder.$file1;
move_uploaded_file($_FILES['cover']['tmp_name'],$cover);
}
else
{
$cover = ''
}
The function rands() does not do anything, so please use it to demonstrate how I can achieve my goal.
If you store your users in database and there is integer user ID, you better use this user ID for file naming rather than separate incrementing counter.
Also you can look at existing files to find maximum existing number like this:
function getNextFileName ()
{
$a = 0;
$b = 2147483647;
while ($a < $b)
{
$c = floor (($a + $b) / 2);
if (file_exists ("$c.jpg")) $a = $c + 1;
else $b = $c;
}
return "$a.jpg";
}
function saveAvatar ($avatar)
{
for ($i = 0; $i < 16; $i++)
{
$name = getNextFileName ();
$fd = fopen ($name, 'x');
if ($fd !== FALSE)
{
fwrite ($fd, $avatar);
fclose ($fd);
return $name;
}
}
return FALSE;
}
for ($i = 0; $i < 20; $i++)
saveAvatar ("BlahBlahBlah$i");
It seem you have problem in genetaing random numbers you can try this:
$prefix = substr(str_shuffle("0123456789"), 0,3);
$file1 = $prefix.'.'.end(explode('.',$_FILES['cover']['name']));
the above $prefix will be like : any random 3 digits
Hope will help it!
/*
* currentDir - path - eg. c:/xampp/htdocs/movies/uploads (no end slash)
* $dir - points current working directory.
* $filename - name of the file.
*/
public static function getFileName($dir, $filename) {
$filePath = $dir . "/uploads/" . $filename;
$fileInfo = pathinfo($filePath);
$i = 1;
while(file_exists($filePath)) {
$filePath = $dir . "/uploads/" . $fileInfo['filename'] . "_" . $i . "." . $fileInfo['extension'];
$i++;
}
return $filePath;
}
move_uploaded_file($_FILES['cover']['tmp_name'],$filePath);
if same filename existing in your uploads folder. it will auto generate the
avatar_1.jpg,
avatar_2.jpg,
avatar_3.jpg, ans so on ..
If (and this is a big IF) your server supports file locking, you can reasonably ensure you have a unique incrementing ID with:
function get_avatar_id()
{
$lockfile = fopen("avatar_id_lock_file","a");
if(flock($lockfile, LOCK_EX)) // Get an exclusive lock to avoid race conditions
{
$avatar_id = intval(file_get_contents("avatar_id"); // Assumes you made it and put a number in it
$avatar_id++;
file_put_contents("avatar_id", $avatar_id);
flock($lockfile, LOCK_UN);
fclose($lockfile);
return $avatar_id;
}
else
{
//What do you want to do if you can't lock the file?
}
}
create an XML file and store the count in there
<? xml version="1.0" ?>
<MyRootNode>
<count>123</count>
</MyRootNode>
UPDATE
Update added after you added more code to your question.
Function Rands(FileExtension as string) as long
'1 open xml file
'2 read counter in
'3 increment counter
'4 save value to back xml file
'5 return incremented counter with the file extension passed attached on the end
'This is in case a BMP GIF or PNG has been and not JPG
' SAMPLE filename 123.GIF
End Function

Count how many files in directory PHP

I'm working on a slightly new project. I wanted to know how many files are in a certain directory.
<div id="header">
<?php
$dir = opendir('uploads/'); # This is the directory it will count from
$i = 0; # Integer starts at 0 before counting
# While false is not equal to the filedirectory
while (false !== ($file = readdir($dir))) {
if (!in_array($file, array('.', '..') and !is_dir($file)) $i++;
}
echo "There were $i files"; # Prints out how many were in the directory
?>
</div>
This is what I have so far (from searching). However, it is not appearing properly? I have added a few notes so feel free to remove them, they are just so I can understand it as best as I can.
If you require some more information or feel as if I haven't described this enough please feel free to state so.
You can simply do the following :
$fi = new FilesystemIterator(__DIR__, FilesystemIterator::SKIP_DOTS);
printf("There were %d Files", iterator_count($fi));
You can get the filecount like so:
$directory = "/path/to/dir/";
$filecount = count(glob($directory . "*"));
echo "There were $filecount files";
where the "*" is you can change that to a specific filetype if you want like "*.jpg" or you could do multiple filetypes like this:
glob($directory . "*.{jpg,png,gif}",GLOB_BRACE)
the GLOB_BRACE flag expands {a,b,c} to match 'a', 'b', or 'c'
Note that glob() skips Linux hidden files, or all files whose names are starting from a dot, i.e. .htaccess.
Try this.
// Directory
$directory = "/dir";
// Returns an array of files
$files = scandir($directory);
// Count the number of files and store them inside the variable..
// Removing 2 because we do not count '.' and '..'.
$num_files = count($files)-2;
You should have :
<div id="header">
<?php
// integer starts at 0 before counting
$i = 0;
$dir = 'uploads/';
if ($handle = opendir($dir)) {
while (($file = readdir($handle)) !== false){
if (!in_array($file, array('.', '..')) && !is_dir($dir.$file))
$i++;
}
}
// prints out how many were in the directory
echo "There were $i files";
?>
</div>
The best answer in my opinion:
$num = count(glob("/exact/path/to/files/" . "*"));
echo $num;
It doesnt counts . and ..
Its a one liner
Im proud of it
Since I needed this too, I was curious as to which alternative was the fastest.
I found that -- if all you want is a file count -- Baba's solution is a lot faster than the others. I was quite surprised.
Try it out for yourself:
<?php
define('MYDIR', '...');
foreach (array(1, 2, 3) as $i)
{
$t = microtime(true);
$count = run($i);
echo "$i: $count (".(microtime(true) - $t)." s)\n";
}
function run ($n)
{
$func = "countFiles$n";
$x = 0;
for ($f = 0; $f < 5000; $f++)
$x = $func();
return $x;
}
function countFiles1 ()
{
$dir = opendir(MYDIR);
$c = 0;
while (($file = readdir($dir)) !== false)
if (!in_array($file, array('.', '..')))
$c++;
closedir($dir);
return $c;
}
function countFiles2 ()
{
chdir(MYDIR);
return count(glob("*"));
}
function countFiles3 () // Fastest method
{
$f = new FilesystemIterator(MYDIR, FilesystemIterator::SKIP_DOTS);
return iterator_count($f);
}
?>
Test run: (obviously, glob() doesn't count dot-files)
1: 99 (0.4815571308136 s)
2: 98 (0.96104407310486 s)
3: 99 (0.26513481140137 s)
Working Demo
<?php
$directory = "../images/team/harry/"; // dir location
if (glob($directory . "*.*") != false)
{
$filecount = count(glob($directory . "*.*"));
echo $filecount;
}
else
{
echo 0;
}
?>
I use this:
count(glob("yourdir/*",GLOB_BRACE))
<?php echo(count(array_slice(scandir($directory),2))); ?>
array_slice works similary like substr function, only it works with arrays.
For example, this would chop out first two array keys from array:
$key_zero_one = array_slice($someArray, 0, 2);
And if You ommit the first parameter, like in first example, array will not contain first two key/value pairs *('.' and '..').
Based on the accepted answer, here is a way to count all files in a directory RECURSIVELY:
iterator_count(
new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator('/your/directory/here/', \FilesystemIterator::SKIP_DOTS)
)
)
$it = new filesystemiterator(dirname("Enter directory here"));
printf("There were %d Files", iterator_count($it));
echo("<br/>");
foreach ($it as $fileinfo) {
echo $fileinfo->getFilename() . "<br/>\n";
}
This should work
enter the directory in dirname. and let the magic happen.
Maybe usefull to someone. On a Windows system, you can let Windows do the job by calling the dir-command. I use an absolute path, like E:/mydir/mysubdir.
<?php
$mydir='E:/mydir/mysubdir';
$dir=str_replace('/','\\',$mydir);
$total = exec('dir '.$dir.' /b/a-d | find /v /c "::"');
$files = glob('uploads/*');
$count = 0;
$totalCount = 0;
$subFileCount = 0;
foreach ($files as $file)
{
global $count, $totalCount;
if(is_dir($file))
{
$totalCount += getFileCount($file);
}
if(is_file($file))
{
$count++;
}
}
function getFileCount($dir)
{
global $subFileCount;
if(is_dir($dir))
{
$subfiles = glob($dir.'/*');
if(count($subfiles))
{
foreach ($subfiles as $file)
{
getFileCount($file);
}
}
}
if(is_file($dir))
{
$subFileCount++;
}
return $subFileCount;
}
$totalFilesCount = $count + $totalCount;
echo 'Total Files Count ' . $totalFilesCount;
Here's a PHP Linux function that's considerably fast. A bit dirty, but it gets the job done!
$dir - path to directory
$type - f, d or false (by default)
f - returns only files count
d - returns only folders count
false - returns total files and folders count
function folderfiles($dir, $type=false) {
$f = escapeshellarg($dir);
if($type == 'f') {
$io = popen ( '/usr/bin/find ' . $f . ' -type f | wc -l', 'r' );
} elseif($type == 'd') {
$io = popen ( '/usr/bin/find ' . $f . ' -type d | wc -l', 'r' );
} else {
$io = popen ( '/usr/bin/find ' . $f . ' | wc -l', 'r' );
}
$size = fgets ( $io, 4096);
pclose ( $io );
return $size;
}
You can tweak to fit your needs.
Please note that this will not work on Windows.
simple code add for file .php then your folder which number of file to count its
$directory = "images/icons";
$files = scandir($directory);
for($i = 0 ; $i < count($files) ; $i++){
if($files[$i] !='.' && $files[$i] !='..')
{ echo $files[$i]; echo "<br>";
$file_new[] = $files[$i];
}
}
echo $num_files = count($file_new);
simple add its done ....

Categories