I have a server folder with a large number of files, randomly named with a guid value (example file: c3c1a48e-a798-41bd-bd70-66ffdc619963.jpg ).
I need to do a case-insensitive search of that folder, as there might be an uppercase (or mixed case) version of the same filename. (I cannot convert existing files to all lowercase file names.)
The answers in this question
PHP Case Insensitive Version of file_exists() provide a function (shown below) that will 'glob' the entire folder into an array, then does a foreach search each item in the array.
This would seem to be a bit slow/inefficient, especially when searching a folder with many (thousands) of files.
Is there a more efficient way to do a case-insensitive filename search? Or is the use of the foreach loop - as shown in the below code - 'efficient enough'?
(This is the code recommended by the above question)
function fileExists($fileName, $caseSensitive = true) {
if(file_exists($fileName)) {
return $fileName;
}
if($caseSensitive) return false;
// Handle case insensitive requests
$directoryName = dirname($fileName);
$fileArray = glob($directoryName . '/*', GLOB_NOSORT);
$fileNameLowerCase = strtolower($fileName);
foreach($fileArray as $file) {
if(strtolower($file) == $fileNameLowerCase) {
return $file;
}
}
return false;
}
I cannot comment, though this can be an answer to your question: No. As of right now in your current state, it looks like you have to use that logic. -However- you could create logic to take those files that have capital letters and use copy($filename,strtolower($filename)) in the folder to make them lower case and then remove the old filenames that have capital letters. . Then in the future upon adding more files to the many, strtolower($new_file_name) before adding the file to the system. I agree with you though that that logic does seem slow, especially with thousands of files.
It contradicts you saying you cannot rename /convert the file names, though once you do it, that will be the only time you would have to rename them.
Related
I want to delete the files which are not appearing in this array. I know the name of file partially but don't know the size parameter suffixed after file name like filename-50x75.jpg, filename-100x77.jpg , filename-500x377.jpg.
I want to delete above images from a directory and don't want to delete below images.
$list_of_allowed_images=array("filename-50x50.jpg","filename-50x70.jpg","filename-90x50.jpg","filename-100x100.jpg","filename-150x150.jpg","filename-250x200.jpg","filename-300x250.jpg","filename-360x270.jpg","filename-390x250.jpg","filename-500x345.jpg","filename-768x576.jpg","filename-820x400.jpg","filename-1024x768.jpg");
I have the following snippet:
foreach(glob($base_path_del.$only_obs_img."[0-9][0-9]*x*.{jpg,gif,png}", GLOB_BRACE) as $file_to_del_now)
{
if(!in_array($file_to_del_now,$list_of_allowed_images))
{
unlink($file_to_del_now);
}
}
but I think it can be more efficient. Is another more efficient way to do this?
Here's what I recommend:
(untested code)
chdir($base_path_del);
$files = glob($only_obs_img."[0-9][0-9]*x*.{jpg,gif,png}", GLOB_BRACE);
$whitelist_regex = "/-(?:50x[57]0|90x50|100x100|150x150|250x200|300x250|360x270|390x250|500x345|768x576|820x400|1024x768)\.jpg$/i";
$removables = preg_grep($whitelist_regex, $files, PREG_GREP_INVERT);
foreach ($removables as $filename) {
unlink($filename);
}
So...
Change the current working directory so that glob() doesn't include the paths in the collection of qualifying files.
Invert preg_grep() so that files that don't match the whitelist regex requirements are retained.
Then just loop the naughty list and delete the lot.
The regex pattern boils down your whitelist array logic. The check starts at the last - in the filename, checks the dimensions, checks .jpg case-insensitively, then ensures that the filename has ended.
p.s. or array_map() if you don't want to break the functional style.
array_map('unlink', $removables);
I have a PHP program which scans a folder named output (which contains all image files in any format) for images. The image files are the screenshots of the output in terminal (I'm using Linux) of various Java programs. The image files are named according to the class name in which the main() function resides. So for example if the main() function resides inside the public Example class, then the output screenshot will be named Example with the extension of either .jpg, .jpeg, .png or .gif. The PHP program has a front page which scans for Java files and lists them in an unordered list with links the user can click to go on the next page. The next page reads the particular file and displays the source code along with the output screenshot. Now since I'm using Fedora and Fedora takes screenshots in png format, that is quite easy like:
echo '<img src="' . $file_name . '".png>'
But what if someone uploads a Java program with the output screenshot in any format? (jpg, jpeg, png, gif). How to then load the appropriate image file since I don't know what the extension will be?
I have an answer to use foreach loop and read through every image file there is within the output folder and then use an if condition for checking the appropriate file names with the various extensions but I think it will not be a very good programming practice.
I generally try to avoid conditions while programming and use more mathematical approach cause that gives me the challenge I need and I feel my code looks different and unique compared to others' but I don't seem to make it work this time.
I'm feeling that this can be done using regular expressions but I don't know how to do it. I know regular expressions but I'm clueless to even how to use them for this. Any answer to not use regular will be appreciated but I want to make this work using regular expressions because in that way I'll also add a little bit of knowledge to my regular expression concepts.
Thanks.
Here's an alternative to MM's that uses RegEx:
function getImageFilename ($basename, $directory) {
$filenames = scandir($directory);
$pattern = "/^" . $basename . "\.(jpeg|png|jpg|gif)$/";
foreach($filenames as $filename) {
preg_match($pattern, $filename, $matches);
if($matches) {
return $filename;
}
}
return false;
}
You can't avoid using a loop. You either loop through the possible file names and check for their existence, or you get a list of all the files in the directory and loop through them whilst performing a pattern match.
If there aren't a lot of files in the directory then this function might perform better because it only needs to call the OS once (to get a list of the files in the directory), whereas asking the OS to check for file existence multiple times requires multiple system calls. (I think that's right...)
One possible solution, you could check if the file exists with that extension (assuming you won't have multiple images with the same name but different extensions):
function get_image($file_name) {
if (file_exists($file_name . '.png')) {
return $file_name . '.png';
} elseif (file_exists($file_name . '.jpg')) {
return $file_name . '.jpg';
} elseif (file_exists($file_name . '.gif')) {
return $file_name . '.gif';
}
return false;
}
echo '<img src="' . get_image($file_name) . '">';
You define the pattern as an or list of the various extensions:
$pattern = '/\.(jpg|png|gif)$/i';
We are also making sure this is an extension by including the match with a dot (escaped) and making sure it's at the end of the string ($). The "i" at the end of that enables case-insensitive matching, so that the regex still picks up GIF or JPG in filenames.
After that, the test is fairly simple:
if (preg_match($pattern, $filename)) {
echo "File $filename is an image";
}
Putting it together in an example, you can see:
$filename = 'test.png';
$pattern = '/\.(jpg|png|gif)$/i';
if (preg_match($pattern, $filename)) {
echo "File $filename is an image";
}
https://eval.in/618651
Whether you want to wrap that in a function, is up to you, as you would have to decide what to return in case the filename does not match one of the extensions provided.
Also note that the test is based on the extension only and not on the content.
I'm trying to group a bunch of files together based on RecipeID and StepID. Instead of storing all of the filenames in a table I've decided to just use glob to get the images for the requested recipe. I feel like this will be more efficient and less data handling. Keeping in mind the directory will eventually contain many thousands of images. If I'm wrong about this then the below question is not necessary lol
So let's say I have RecipeID #5 (nachos, mmmm) and it has 3 preparation steps. The naming convention I've decided on would be as such:
5_1_getchips.jpg
5_2_laycheese.jpg
5_2_laytomatos.jpg
5_2_laysalsa.jpg
5_3_bake.jpg
5_finishednachos.jpg
5_morefinishedproduct.jpg
The files may be generated by a camera, so DSC###.jpg...or the person may have actually named each picture as I have above. Multiple images can exist per step. I'm not sure how I'll handle dupe filenames, but I feel that's out of scope.
I want to get all of the "5_" images...but filter them by all the ones that DON'T have any step # (grouped in one DIV), and then get all the ones that DO have a step (grouped in their respective DIVs).
I'm thinking of something like
foreach ( glob( $IMAGES_RECIPE . $RecipeID . "-*.*") as $image)
and then using a substr to filter out the step# but I'm concerned about getting the logic right because what if the original filename already has _#_ in it for some reason. Maybe I need to have a strict naming convention that always includes _0_ if it doesn't belong to a step.
Thoughts?
Globbing through 1000s of files will never being faster than having indexed those files in a database (of whatever type) and execute a database query for them. That's what databases are meant for.
I had a similar issue with 15,000 mp3 songs.
In the Win command line dir
dir *.mp3 /b /s > mp3.bat
Used a regex search and replace in NotePad++ that converted the the file names and prefixed and appended text creating a Rename statement and Ran the mp3.bat.
Something like this might work for you in PHP:
Use regex to extract the digits using preg_replace to
Create a logic table(s) to create the words for the new file names
create the new filename with rename()
Here is some simplified and UNTESTED Example code to show what I am suggesting.
Example Logic Table:
$translation[x][y][z] = "phrase";
$translation[x][y][z] = "phrase";
$translation[x][y][z] = "phrase";
$translation[x][y][z] = "phrase";
$folder = '/home/user/public_html/recipies/';
$dir=opendir($folder);
while (false !== ($found=readdir($dir))){
if pathinfo($file,PATHINFO_EXTENSION) == '.jpg')
{
$files[]= pathinfo($file,PATHINFO_FILENAME);
}
}
foreach($files as $key=> $filename){
$digit1 = 'DSC(\d)\d\d\.jpg/',"$1", $filename);
$digit2 = 'DSC\d(\d)\d\.jpg',"$1", $filename);
$digit3 = 'DSC\d\d(\d)\.jpg',"$1", $filename);
$newName = $translation[$digit1][$digit2][$digit3]
ren($filename,$newfilename);
}
This was a poorly worded question
My main goal was to display many images or even a range from a set arbitrarily, renaming the files to sequential numbers seemed like it would make the displaying or iterating through the files easier if they just differed by 1 rather than random strings.
-- anyway I'm going to read... seems glob is according to php manual ... The glob() function searches for all the pathnames matching pattern according to the rules used by the libc glob() function
The files have random names but they are all .jpg's
As an example, "this name".jpg is replaced with "i+1".jpg
So that I can display the photos lazily using a for loop incrementing the numbers. The primary purpose is to display the photos regardless of their file names.
You can use php glob and rename functions:
$num = 1;
foreach (glob("/path/to/images/*.jpg") as $filename) {
$fileNoExtension = basename($filename, ".jpg");
rename ($filename, "$fileNoExtension{$num}.jpg");
$num++;
}
Basically,
$count = 1;
foreach (glob('*.jpg') as $filename) {
#rename($filename, $count.'.jpg');
$count++;
}
echo 'Done';
All image files must be in the current folder (along with the script).
You can add a path manually, if you wish:
glob('/path/to/*.jpg')
rename('path/..'.$file etc. )
I want to create a piece of code that will remove images based on set parameters and a wildcard.
The number of images and naming will vary, though the first two parameters will remain as a constant.
// Example of images to be deleted / removed.
/images/1-50-variablename-A.jpg
/images/1-50-variablename-B.jpg
/images/1-50-variablename-C.jpg
/images/1-50-variablename-D.jpg
/images/1-50-variablename-E.jpg
Essentially I am after a loop to make this happen though I am not too sure on the best logic to make this happen.
$menuid = "1";
$imageid = "50"
$fileName = "images/".$menuid."-".$imageid."-*.jpg";
if (file_exists ($fileName)) {
unlink ($fileName);
}
You can use the php glob function (http://php.net/manual/fr/function.glob.php). Feed it with your pattern (it supports wildcards) and then iterate over the result and unlink each file.
Hope it helped
Solution presented itself in Glob form.
$menuid ="9999";
$imageid="5";
array_map('unlink', glob("../images/".$menuid."-".$imageid."-*.jpg"));