Filtering filenames in PHP - php

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);
}

Related

Case Insensitive file_exists for large list of files

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.

How can I bulk rename files in a photo folder with sequential numbers

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. )

Create Unique Image Names

What's a good way to create a unique name for an image that my user is uploading?
I don't want to have any duplicates so something like MD5($filename) isn't suitable.
Any ideas?
as it was mentioned, i think that best way to create unique file name is to simply add time(). that would be like
$image_name = time()."_".$image_name;
Grab the file extension from uploaded file:
$ext = pathinfo($uploaded_filename, PATHINFO_EXTENSION);
Grab the time to the second: time()
Grab some randomness: md5(microtime())
Convert time to base 36: base_convert (time(), 10, 36) - base 36 compresses a 10 byte string down to about 6 bytes to allow for more of the random string to be used
Send the whole lot out as a 16 char string:
$unique_id = substr( base_convert( time(), 10, 36 ) . md5( microtime() ), 0, 16 ) . $ext;
I doubt that will ever collide - you could even not truncate it if you don't mind very long file names.
If you actually need a filename (it's not entirely clear from your question) I would use tempnam(), which:
Creates a file with a unique filename, with access permission set to 0600, in the specified directory.
...and let PHP do the heavy lifting of working out uniqueness. Note that as well as returning the filename, tempnam() actually creates the file; you can just overwrite it when you drop the image file there.
You could take a hash (e.g., md5, sha) of the image data itself. That would help identify duplicate images too (if it was byte-for-byte, the same). But any sufficiently long string of random characters would work.
You can always rig it up in a way that the file name looks like:
/image/0/1/012345678/original-name.jpg
That way the file name looks normal, but it's still unique.
I'd recommend sha1_file() over md5_file(). It's less prone to collisions.
You could also use hash_file('sha256', $filePath) to get even better results.
http://php.net/manual/en/function.uniqid.php maybe?
You can prefix it with the user id to avoid collisions between 2 users (in less than one millisecond).
For short names:
$i = 0;
while(file_exists($name . '_' . $i)){
$i++;
}
WARNING: this might fail on a multi threaded server if two user upload a image with the same name at the same time.
In that case you should include the md5 of the username.
lol there are around 63340000000000000000000000000000000000000000000000 possibility's that md5 can produce
plus you could use just tobe on the safe side
$newfilename = md5(time().'image');
if(file_exists('./images/'.$newfilename)){
$newfilename = md5(time().$newfilename);
}
//uploadimage
How big is the probablity of two users uploading image with same name on same microsecond ?
try
$currTime = microtime(true);
$finalFileName = cleanTheInput($fileName)."_".$currTime;
// you can also append a _.rand(0,1000) in the end to have more foolproof name collision
function cleanTheInput($input)
{
// do some formatting here ...
}
This would also help you in tracking the upload time of the file for analysis. or may be sort the files,manage the files.
For good performance and uniqueness you can use approach like this:
files will be stored on a server with names like md5_file($file).jpg
the directory to store file in define from md5 file name, by stripping first two chars (first level), and second two (second level) like that:
uploaded_files\ 30 \ c5 \ 30 c5 67139b64ee14c80cc5f5006d8081.pdf
create record in database with file_id, original file name, uploaded user id, and path to file on server
on server side create script that'll get role of download providing - it'll get file by id from db, and output its content with original filename provided by user (see php example of codeigniter download_helper ). So url to file will look like that:
http://site.com/download.php?file=id
Pros:
minified collisions threat
good performance at file lookup (not much files in 1 directory, not much directories at the same level)
original file names are saved
you can adjust access to files by server side script (check session or cookies)
Cons:
Good for small filesizes, because before user can download file, server have to read this file in memory
try this file format:
$filename = microtime(true) . $username . '.jpg';
I think it would be good for you.
<?php
$name=uniqid(mt_rand()).$image_name;
?>
You should try to meet two goals: Uniqueness, and usefulness.
Using a GUID guarantees uniqueness, but one day the files may become detached from their original source, and then you will be in trouble.
My typical solution is to embed crucial information into the filename, such as the userID (if it belongs to a user) or the date and time uploaded (if this is significant), or the filename used when uploading it.
This may really save your skin one day, when the information embedded in the filename allows you to, for example, recover from a bug, or the accidental deletion of records. If all you have is GUIDs, and you lose the catalogue, you will have a heck of a job cleaning that up.
For example, if a file "My Holiday: Florida 23.jpg" is uploaded, by userID 98765, on 2013/04/04 at 12:51:23 I would name it something like this, adding a random string ad8a7dsf9:
20130404125123-ad8a7dsf9-98765-my-holiday-florida-23.jpg
Uniqueness is ensured by the date and time, and random string (provided it is properly random from /dev/urandom or CryptGenRandom.
If the file is ever detached, you can identify the user, the date and time, and the title.
Everything is folded to lower case and anything non-alphanumeric is removed and replaced by dashes, which makes the filename easy to handle using simple tools (e.g. no spaces which can confuse badly written scripts, no colons or other characters which are forbidden on some filesystems, and so on).
Something like this could work for you:
while (file_exists('/uploads/' . $filename . '.jpeg')) {
$filename .= rand(10, 99);
}
Ready-to-use code:
$file_ext = substr($file['name'], -4); // e.g.'.jpg', '.gif', '.png', 'jpeg' (note the missing leading point in 'jpeg')
$new_name = sha1($file['name'] . uniqid('',true)); // this will generate a 40-character-long random name
$new_name .= ((substr($file_ext, 0, 1) != '.') ? ".{$file_ext}" : $file_ext); //the original extension is appended (accounting for the point, see comment above)

How can I make a simple path generator?

I have a link that I have to repeat 50 times, for each folder, and I have 15 folders.the link that I have to repeat looks like this:
now, the jpg files are named car 1- car 50. and I would really like to be able to generate this script so that I can input the path "update/images/Cars/" the picture title (car) and the input the number of times that I need this link, and then have it spit out something that looks like this:
and then it keeps repeating, I'm assuming this can be done with a counter, but I'm not sure. Thanks!
You can do it with a for loop:
$path = "update/images/Cars/";
$title = "car";
$times = 50;
for($i = 1; $i <= $times; $i++)
echo "\n";
I used $title for the lightbox argument since you didn't specify
Use a powerful text editor. ;-)
For example, in Vim, I can use the following sequence of keystrokes to create your required text:
i
Esc
qa (start recording macro into register a)
Y (yank (= copy) whole line)
p (paste into the following line)
/ ( Return (search for opening brace)
Space (advance cursor one character so it now sits on the number)
Ctrl+a (increment the number)
q (stop recording the macro)
49#a (invoke the macro 49 times)
If you're going to add or remove images from the folder, then you might get better results using the DirectoryIterator object from the Standard PHP Library. Using it would require PHP5, but there's an old-school way of handling it, too. This snippet assumes that all of the files in the directory are the images you want to list:
$link = '%s';
$dir = new DirectoryIterator("/path/to/update/images/Cars");
foreach($dir as $file) if(!$file->isDot()) echo sprintf($link, $file, $file);
Notice that I put the information about the anchor-element into the $link variable and then used sprintf to print those anchors to the screen. If you don't have PHP5 available to you, you'd want to do it this way:
$link = '%s';
$dir = opendir("/path/to/update/images/Cars");
while(($file = readdir($dir)!==false) if($file != "." && $file != "..") echo sprintf($link, $file, $file);
closedir($dir);
These would only be necessary if you're adding more car photos into the library and don't want to update the page that produces all the links. Both of these snippets should automatically search through the directory of car images and create the links you need.
You can also alter these snippets to search through sub-directories, so you could slam out the links to the images in all 15 folders all with a little bit more code. Let me know if you want to see that code, too.

PHP - Open or copy a file when knowing only part of its name?

I have a huge repository of files that are ordered by numbered folders. In each folder is a file which starts with a unique number then an unknown string of characters. Given the unique number how can i open or copy this file?
for example:
I have been given the number '7656875' and nothing more.
I need to interact with a file called '\server\7656800\7656875 foobar 2x4'.
how can i achieve this using PHP?
If you know the directory name, consider using glob()
$matches = glob('./server/dir/'.$num.'*');
Then if there is only one file that should start with the number, take the first (and only) match.
Like Yacoby suggested, glob should do the trick. You can have multiple placeholders in it as well, so if you know the depth, but not the correct naming, you can do:
$matchingFiles = glob('/server/*/7656875*');
which would match
"/server/12345/7656875 foo.txt"
"/server/56789/7656875 bar.jpg"
but not
"/server/12345/subdir/7656875 foo.txt"
If you do not know the depth glob() won't help, you can use a RecursiveDirectoryIterator passing in the top most folder path, e.g.
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/server'));
foreach($iterator as $fileObject) {
// assuming the filename begins with the number
if(strpos($fileObject->getFilename(), '7656875') === 0) {
// do something with the $fileObject, e.g.
copy($fileObject->getPathname(), '/somewhere/else');
echo $fileObject->openFile()->fpassthru();
}
}
* Note: code is untested but should work
DirectoryIterator return SplFileInfo objects, so you can use them to directly access the files through a high-level API.
$result = system("ls \server\" . $specialNumber . '\');
$fh = fopen($result, 'r');
If it's hidden below in sub-sub-directories of variable length, use find
echo `find . -name "*$input*"`;
Explode and trim each result, then hope you found the correct one.

Categories