How to find a file recursively in folders using PHP? - php

How to find a file in folders with sub name of the file means the file need to be searched is not fully named but it is a part of the name for example I want to search the file namely 'hello_world_123.png'. I did not enter full name of the file but one or some words only, for example the 'world_' is my input but the code should search the full name of file.
In short the searching may work like operating system's search in file explorer or file manager.
My code as below but I am stuck with matching the name of file in if condition.
$file_name_ = 'recursive-1.php';
$dir = new RecursiveDirectoryIterator( $folder_path, RecursiveDirectoryIterator::SKIP_DOTS);
$it = new RecursiveIteratorIterator($dir);
foreach($it as $file) {
$file_info = pathinfo($file);
$patt = '/^(\w+)\.+('.$file_name_.')\.+(\w+)*$/';
$result = preg_match($patt,strtolower( $file_info['basename'] ),$match );
if ( $result ) {
echo 'File found';
} else {
echo 'File not found';
}
}

To find the file in directory recursively, with or without extension, the following code is helpful.
The file name is not complete name (the part of the file name) so the strpos() function will find the position of file name which is partial.
$file_name_ = 'recursive-1.php';
//Splitting the file to get the file name without extension
$file_name_ = explode('.',$file_name_);
$file_name_ = $file_name_[0];
$dir = new RecursiveDirectoryIterator( $folder_path, RecursiveDirectoryIterator::SKIP_DOTS);
$it = new RecursiveIteratorIterator($dir);
//looping for all files in given directory
foreach($it as $file) {
$file_info = pathinfo($file);
//$patt = '/^(\w+)\.+('.$file_name_.')\.+(\w+)*$/';
//$result = preg_match($patt,strtolower( $file_info['basename'] ),$match );
//$result = strpos(strtolower($file_info['filename']),strtolower( $file_name_));
$result = stripos($file_info['filename'], $file_name_);
if ( $result === flase ) {
echo 'File not found';
} else {
echo 'File found';
}
}

You should use a preg_match() for that. You can look if the file in the folder has part or the full name of the file you are searching for.

Related

How to save extracted zip file with specific filename

I have zip files with only one file inside it, but it has new name every time. I need to extract file and save it with specific file name, not extracted one.
$zip = new ZipArchive;
$res = $zip->open($tmp_name);
if ($res === TRUE) {
$path = _PATH."/files/";
$zip->extractTo($path);
$zip->close();
echo 'Unzip!';
}
Abowe code works, but I need to have specific filename. For example anyfile located under zip (eg. pricelist025.xml should be named temp.xml
Rename your specific file before you extract it.
$zip = new ZipArchive;
$res = $zip->open($tmp_name);
if ($res === TRUE) {
$zip->renameName('pricelist025.xml','temp.xml');
$path = _PATH."/files/";
$zip->extractTo($path);
$zip->close();
echo 'Unzip!';
} else {
echo 'failed, code:' . $res;
}
I hope this works.
UPDATE 1
There is two options if you want to change the file names.
1. change the file names before extracting -This way zip files will be modified
2. change the file names after extracting -Zip files will remain as they were before
Changing file names before extracting
We have to define a pattern for filenames. Here, Files will be in this patter : myfile0.xml, myfile1.html, adn so on..
Note: extension will be preserved.
$zip = new ZipArchive;
$res = $zip->open('hello.zip');
$newfilename = 'myfile';
for($i=0;$i<$zip->count();$i++)
{
$extension = pathinfo($zip->getNameIndex($i))['extension'];
$zip->renameName($zip->getNameIndex($i), $newfilename.$i.'.'.$extension);
}
Chaning file names after extracting
File names will in the same pattern as above.
$directory = 'hello/'; //your extracted directory
$newfilename = 'myfile';
foreach (glob($directory."*.*") as $index=>$filename) {
$basename = pathinfo($filename)['basename'];
if(!preg_match('/myfile\d\./', $basename)) {
$extension = pathinfo($filename)['extension'];
rename($filename,$newfilename.$index.'.'.$extension);
}
}
What we are here scanning the all the files from the extracted directory for which doesn't have a filename in the patter myfile[num]. and then we are changing it's name.
UPDATE 2
I just noticed you have updated your question.
As you have just one file and you want to extract it every time with different name. You should rename it every time you extract.
$zip = new ZipArchive;
$newfilename = "myfile".rand(1,999); //you can define any safe pattern here that suites you
if($zip->open('help.zip')===TRUE)
{
$path = '/your/path/to/directory';
$filename = $zip->getNameIndex(0);
if($zip->extractTo($path))
{
echo "Extracted";
}else{
echo "Extraction Failed";
exit();
}
$extension = pathinfo($filename)['extension'];
rename($path."/$filename",$path."/$newfilename".'.'.$extension);
echo "Extracted with different name successfully!";
} else {
echo "Failed";
}

"the system could not find the file specified" when loops on folder and rename each file

I'm looping unto directories and each directory consist of multiple files that yet to be rename. Below is the code
<?php
$path = __DIR__.'/';
$files = array_diff(scandir($path), array('.', '..'));
foreach($files as $f ){
$files2 = array_diff(scandir($path.'/'.$f), array('.', '..'));
foreach( $files2 as $f2 ){
rename($path.'/'.$f.'/'.$f2, $path.'/'.$f.'/'.strtolower(str_replace(' ','_',$f2)));
echo 'success<br>';
}
}
above codes return an error of
The system cannot find the file specified. (code: 2)
in each directory, some of the files has the name that consist of special character(s) e.g. Velāyat-e Nūrestān.json.
Any ideas ?
Your code,in the actual state can't work because there are a lot of issues in it.I suggest to use native Directory Iterator to achieve this properly .
You can use:
$root=__DIR__.'/';
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root));//create a recursive directory iterator
$it->rewind();
while($it->valid())
{
if (
!$it->isDot()//if file basename not in ['.','..']
&&$it->isFile()//and is really a file and not a directory
)
{
rename(str_replace('/','\\',$it->getPathname()),str_replace('/','\\',$it->getPath().'\\'.mb_strtolower(str_replace(' ','_',$it->getBasename()))));//try to rename it
echo "success";
}
$it->next();
}

On creating zip file by php I get two files instead of one

I'm struggling around with a simple PHP functionality: Creating a ZIP Archive with some files in.
The problem is, it does not create only one file called filename.zip but two files called filename.zip.a07600 and filename.zip.b07600. Pls. see the following screenshot:
The two files are perfect in size and I even can rename each of them to filename.zip and extract it without any problems.
Can anybody tell me what is going wrong???
function zipFilesAndDownload_Defect($archive_file_name, $archiveDir, $file_path = array(), $files_array = array()) {
// Archive File Name
$archive_file = $archiveDir."/".$archive_file_name;
// Time-to-live
$archiveTTL = 86400; // 1 day
// Delete old zip file
#unlink($archive_file);
// Create the object
$zip = new ZipArchive();
// Create the file and throw the error if unsuccessful
if ($zip->open($archive_file, ZIPARCHIVE::CREATE) !== TRUE) {
$response->res = "Cannot open '$archive_file'";
return $response;
}
// Add each file of $file_name array to archive
$i = 0;
foreach($files_array as $value){
$expl = explode("/", $value);
$file = $expl[(count($expl)-1)];
$path_file = $file_path[$i] . "/" . $file;
$size = round((filesize ($path_file) / 1024), 0);
if(file_exists($path_file)){
$zip->addFile($path_file, $file);
}
$i++;
}
$zip->close();
// Then send the headers to redirect to the ZIP file
header("HTTP/1.1 303 See Other"); // 303 is technically correct for this type of redirect
header("Location: $archive_file");
exit;
}
The code which calls the function is a file with a switch-case... it is called itself by an ajax-call:
case "zdl":
$files_array = array();
$file_path = array();
foreach ($dbh->query("select GUID, DIRECTORY, BASENAME, ELEMENTID from SMDMS where ELEMENTID = ".$osguid." and PROJECTID = ".$osproject.";") as $subrow) {
$archive_file_name = $subrow['ELEMENTID'].".zip";
$archiveDir = "../".$subrow['DIRECTORY'];
$files_array[] = $archiveDir.DIR_SEPARATOR.$subrow['BASENAME'];
$file_path[] = $archiveDir;
}
zipFilesAndDownload_Defect($archive_file_name, $archiveDir, $file_path, $files_array);
break;
One more code... I tried to rename the latest 123456.zip.a01234 file to 123456.zip and then unlink the old 123456.zip.a01234 (and all prior added .a01234 files) with this function:
function zip_file_exists($pathfile){
$arr = array();
$dir = dirname($pathfile);
$renamed = 0;
foreach(glob($pathfile.'.*') as $file) {
$path_parts = pathinfo($file);
$dirname = $path_parts['dirname'];
$basename = $path_parts['basename'];
$extension = $path_parts['extension'];
$filename = $path_parts['filename'];
if($renamed == 0){
$old_name = $file;
$new_name = str_replace(".".$extension, "", $file);
#copy($old_name, $new_name);
#unlink($old_name);
$renamed = 1;
//file_put_contents($dir."/test.txt", "old_name: ".$old_name." - new_name: ".$new_name." - dirname: ".$dirname." - basename: ".$basename." - extension: ".$extension." - filename: ".$filename." - test: ".$test);
}else{
#unlink($file);
}
}
}
In short: copy works, rename didn't work and "unlink"-doesn't work at all... I'm out of ideas now... :(
ONE MORE TRY: I placed the output of $zip->getStatusString() in a variable and wrote it to a log file... the log entry it produced is: Renaming temporary file failed: No such file or directory.
But as you can see in the graphic above the file 43051221.zip.a07200 is located in the directory where the zip-lib opens it temporarily.
Thank you in advance for your help!
So, after struggling around for days... It was so simple:
Actually I work ONLY on *nix Servers so in my scripts I created the folders dynamically with 0777 Perms. I didn't know that IIS doesn't accept this permissions format at all!
So I remoted to the server, right clicked on the folder Documents (the hierarchically most upper folder of all dynamically added files and folders) and gave full control to all users I found.
Now it works perfect!!! The only thing that would be interesting now is: is this dangerous of any reason???
Thanks for your good will answers...
My suspicion is that your script is hitting the PHP script timeout. PHP zip creates a temporary file to zip in to where the filename is yourfilename.zip.some_random_number. This file is renamed to yourfilename.zip when the zip file is closed. If the script times out it will probably just get left there.
Try reducing the number of files to zip, or increasing the script timeout with set_time_limit()
http://php.net/manual/en/function.set-time-limit.php

Linux PHP ExtractTo returns whole path instead of the file structure

I am pulling my hair out over here. I have spent the last week trying to figure out why the ZipArchive extractTo method behaves differently on linux than on our test server (WAMP).
Below is the most basic example of the problem. I simply need to extract a zip that has the following structure:
my-zip-file.zip
-username01
--filename01.txt
-images.zip
--image01.png
-songs.zip
--song01.wav
-username02
--filename01.txt
-images.zip
--image01.png
-songs.zip
--song01.wav
The following code will extract the root zip file and keep the structure on my WAMP server. I do not need to worry about extracting the subfolders yet.
<?php
if(isset($_FILES["zip_file"]["name"])) {
$filename = $_FILES["zip_file"]["name"];
$source = $_FILES["zip_file"]["tmp_name"];
$errors = array();
$name = explode(".", $filename);
$continue = strtolower($name[1]) == 'zip' ? true : false;
if(!$continue) {
$errors[] = "The file you are trying to upload is not a .zip file. Please try again.";
}
$zip = new ZipArchive();
if($zip->open($source) === FALSE)
{
$errors[]= "Failed to open zip file.";
}
if(empty($errors))
{
$zip->extractTo("./uploads");
$zip->close();
$errors[] = "Zip file successfully extracted! <br />";
}
}
?>
The output from the script above on WAMP extracts it correctly (keeping the file structure).
When I run this on our live server the output looks like this:
--username01\filename01.txt
--username01\images.zip
--username01\songs.zip
--username02\filename01.txt
--username02\images.zip
--username02\songs.zip
I cannot figure out why it behaves differently on the live server. Any help will be GREATLY appreciated!
To fix the file paths you can iterate over all extracted files and move them.
Supposing inside your loop over all extracted files you have a variable $source containing the file path (e.g. username01\filename01.txt) you can do the following:
// Get a string with the correct file path
$target = str_replace('\\', '/', $source);
// Create the directory structure to hold the new file
$dir = dirname($target);
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
// Move the file to the correct path.
rename($source, $target);
Edit
You should check for a backslash in the file name before executing the logic above. With the iterator, your code should look something like this:
// Assuming the same directory in your code sample.
$dir = new DirectoryIterator('./uploads');
foreach ($dir as $fileinfo) {
if (
$fileinfo->isFile()
&& strpos($fileinfo->getFilename(), '\\') !== false // Checking for a backslash
) {
$source = $fileinfo->getPathname();
// Do the magic, A.K.A. paste the code above
}
}

php - extract files from folder in a zip

I have a zip file containing one folder, that contains more folders and files, like this:
myfile.zip
-firstlevel
--folder1
--folder2
--folder3
--file1
--file2
Now, I want to extract this file using PHPs ZipArchive, but without the "firstlevel" folder. At the moment, the results look like this:
destination/firstlevel/folder1
destination/firstlevel/folder2
...
The result I'd like to have would look like this:
destination/folder1
destination/folder2
...
I've tried extractTo, which produces the first mentioned result, and copy(), as suggested here, but this doesn't seem to work at all.
My current code is here:
if($zip->open('myfile.zip') === true) {
$firstlevel = $zip->getNameIndex(0);
for($i = 0; $i < $zip->numFiles; $i++) {
$entry = $zip->getNameIndex($i);
$pos = strpos($entry, $firstlevel);
if ($pos !== false) {
$file = substr($entry, strlen($firstlevel));
if(strlen($file) > 0){
$files[] = $file;
}
}
}
//attempt 1 (extractTo):
//$zip->extractTo('./test', $files);
//attempt 2 (copy):
foreach($files as $filename){
copy('zip://'.$firstlevel.'/'.$filename, 'test/'.$filename);
}
}
How can I achieve the result I'm aiming for?
Take a look at my Quick Unzipper script. I wrote this for personal use a while back when uploading large zip files to a server. It was a backup, and 1,000s of files take forever with FTP so using a zip file was faster. I use Git and everything, but there wasn't another option for me. I place this php file in the directory I want the files to go, and put the zip file in the same directory. For my script, they all have to operate in the same directory. It was an easy way to secure it for my needs, as everything I needed was in the same dir.
Quick Unzipper: https://github.com/incomepitbull/QuickUnzipper/blob/master/unzip.php
I linked the file because I am not showcasing the repo, just the code that makes the unzip tick. With modern versions of PHP, there should't be anything that isn't included on your setup. So you shouldn't need to do any server config changes to use this.
Here is the PHP Doc for the ZipArchive class it uses: http://php.net/manual/en/class.ziparchive.php
There isn't any included way to do what you want, which is a shame. So I would unzip the file to a temp directory, then use another function to copy the contents to where you want. So when using ZipArchive, you will need to return the first item to get the folder name if it is unknown. If the folder is known, ie: the same pesky folder name every time, then you could hard code the name.
I have made it return the first item from the index. So if you ALWAYS have a zip with 1 folder inside it, and everything in that folder, this would work. However, if you have a zip file without everything consolidated inside 1 folder, it would fail. The code I have added will take care of your question. You will need to add further logic to handle alternate cases.
Also, You will still be left with the old directory from when we extract it to the temp directory for "processing". So I included code to delete it too.
NOTE: The code uses a lot of if's to show the processing steps, and print a message for testing purposes. You would need to modify it to your needs.
<?php
public function copyDirectoryContents($source, $destination, $create=false)
{
if ( ! is_dir($source) ) {
return false;
}
if ( ! is_dir($destination) && $create === true ) {
#mkdir($destination);
}
if ( is_dir($destination) ) {
$files = array_diff(scandir($source), array('.','..'));
foreach ($files as $file)
{
if ( is_dir($file) ) {
copyDirectoryContents("$source/$file", "$destination/$file");
} else {
#copy("$source/$file", "$destination/$file");
}
}
return true;
}
return false;
}
public function removeDirectory($directory, $options=array())
{
if(!isset($options['traverseSymlinks']))
$options['traverseSymlinks']=false;
$files = array_diff(scandir($directory), array('.','..'));
foreach ($files as $file)
{
if (is_dir("$directory/$file"))
{
if(!$options['traverseSymlinks'] && is_link(rtrim($file,DIRECTORY_SEPARATOR))) {
unlink("$directory/$file");
} else {
removeDirectory("$directory/$file",$options);
}
} else {
unlink("$directory/$file");
}
}
return rmdir($directory);
}
$file = dirname(__FILE__) . '/file.zip'; // full path to zip file needing extracted
$temp = dirname(__FILE__) . '/zip-temp'; // full path to temp dir to process extractions
$path = dirname(__FILE__) . '/extracted'; // full path to final destination to put the files (not the folder)
$firstDir = null; // holds the name of the first directory
$zip = new ZipArchive;
$res = $zip->open($file);
if ($res === TRUE) {
$firstDir = $zip->getNameIndex(0);
$zip->extractTo($temp);
$zip->close();
$status = "<strong>Success:</strong> '$file' extracted to '$temp'.";
} else {
$status = "<strong>Error:</strong> Could not extract '$file'.";
}
echo $status . '<br />';
if ( empty($firstDir) ) {
echo 'Error: first directory was empty!';
} else {
$firstDir = realpath($temp . '/' . $firstDir);
echo "First Directory: $firstDir <br />";
if ( is_dir($firstDir) ) {
if ( copyDirectoryContents($firstDir, $path) ) {
echo 'Directory contents copied!<br />';
if ( removeDirectory($directory) ) {
echo 'Temp directory deleted!<br />';
echo 'Done!<br />';
} else {
echo 'Error deleting temp directory!<br />';
}
} else {
echo 'Error copying directory contents!<br />';
}
} else {
echo 'Error: Could not find first directory';
}
}

Categories