php obtaining complex file extension like mysql-5.0.1.tar.gz - php

I've run into a typical problem here. Till now i was doing
strstr($filename,".");
$filename is the file i got from $_FILE i.e uploaded file.It was running fine until i hit a filename of the type i mentioned.
Even doing
pathinfo($filename);
gives me
.gz
I need to see whether it is EXACTLY
.tar.gz

Technically, pathinfo is correct: the one and only extension for that file is .gz. The fact that it has .tar in the name is as coincidental as the fact that it has 5.0.1 in it.
That doesn't make your interest in checking for .tar.gz files invalid, but it does raise the question: what specifically do you want to find?
The most direct solution to your specific question is: first look for the extension (via pathinfo or the strpos function) and then if it happens to be .gz look for the "extension" in the remaining filename (via the same technique).
$parts = pathinfo($filename);
$extension = $parts['extension'];
if ($extension == '.gz') {
$parts = pathinfo($parts['filename']);
$extension = $parts['extension'] . $extension;
}

The most simple way would be to check for the last 7 characters of the filename - this ensures that every file ends with .tar.gz:
if (substr($filename, -7) == '.tar.gz') {
// continue
}

And if you needed to parse 'this.is.a.very.long.file.name.with.lots.of.full.stops' then what part of that is the file extension? Relying on a particular part of a filename to convey semantic, machine readable information about the contents of the file is, at best dangerous.
It's not clear what the problem you are trying is - why do you need to know what the extension is?
C.

I just posted the following enhanced version of pathinfo() in comments over on PHP.net. This version groups all of the parts of the file extension in as the extension (e.g. "tar.gz" instead of just "gz"), leaving the rest as the filename:
<?php
function pathinfo_enhanced($file_path) {
$core_path_info = pathinfo($file_path);
$filename = $core_path_info['filename'];
if (isset($core_path_info['extension'])) {
$extension = $core_path_info['extension'];
} else {
$extension = '';
}
$extension_parts = array();
while (!empty($extension)) {
array_unshift($extension_parts, $extension);
$remaining_path_info = pathinfo($filename);
$filename = $remaining_path_info['filename'];
if (isset($remaining_path_info['extension'])) {
$extension = $remaining_path_info['extension'];
} else {
$extension = '';
}
}
$revised_path_info = array(
'filename' => $filename,
'extension' => implode('.', $extension_parts),
);
return array_merge($core_path_info, $revised_path_info);
}
Here are some examples you can run to show how it handles the different cases:
// Directory; two extensions
$path = '/www/htdocs/inc/file.tar.gz';
$info = pathinfo_enhanced($path);
echo "$path\n";
print_r($info);
echo "\n";
// Directory; one extension
$path = '/www/htdocs/inc/file.tgz';
$info = pathinfo_enhanced($path);
echo "$path\n";
print_r($info);
echo "\n";
// Directory; no extension
$path = '/www/htdocs/inc/lib';
$info = pathinfo_enhanced($path);
echo "$path\n";
print_r($info);
echo "\n";
// No directory; one extension
$path = 'test.php';
$info = pathinfo_enhanced($path);
echo "$path\n";
print_r($info);
echo "\n";
// No directory; dot file
$path = '.example';
$info = pathinfo_enhanced($path);
echo "$path\n";
print_r($info);
echo "\n";
// Directory only
$path = '/www/htdocs/inc/';
$info = pathinfo_enhanced($path);
echo "$path\n";
print_r($info);
echo "\n";

THIS is the answer:
strstr(pathinfo('asdasd/qweqwe/asdasd.tar.gz')['basename'], '.');
will return you '.tar.gz'

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

file_exists() not working in php5 inside while loop

file_exists isn't working. I've looked at a few examples and still no go. Program does not detect the file. The path of my file is /var/www/osbs/PHPAPI/recording.mp3 and the website root is inside osbs. The location of this file is inside PHPAPI that is why I do not put full path in file_put_contents. The program is able to make the original recording.mp3 but not any appended versions of it.
<?php
$actual_name = pathinfo("PHPAPI/recording.mp3",PATHINFO_FILENAME);
$original_name = $actual_name;
$extension = pathinfo("PHPAPI/recording.mp3",PATHINFO_EXTENSION);
if ($_GET["RecordingUrl"]) {
if (file_exists("/var/www/osbs/PHPAPI/".$actual_name.".".$extension)) {
$actual_name = find_new_name($original_name, $extension);
}
else {
$actual_name = $original_name;
}
$name = $actual_name.".".$extension;
file_put_contents($name, file_get_contents($_GET["RecordingUrl"]));
}
function find_new_name ( $file, $extension ) {
$name = $file.".".$extension;
$i = 0;
while(file_exists("/var/www/osbs/PHPAPI/".$name)){
$new_name = $file.$i;
$name = $new_name.".".$extension;
$i++;
}
return $new_name;
}
?>
Your issue is with the file_put_contents. You need to specify a full path, and you only specify a file name. Try echoing $name just before using it, you'll see it's not a path, just a filename.
I would recommend you to set a constant at the begining of the file with the path instead of sometimes relying on relative paths and sometimes relying on absolute paths.
<?php
const SAVE_PATH = "/var/www/osbs/";
$actual_name = pathinfo(SAVE_PATH."PHPAPI/recording.mp3",PATHINFO_FILENAME);
$original_name = $actual_name;
$extension = pathinfo(SAVE_PATH."PHPAPI/recording.mp3",PATHINFO_EXTENSION);
if (isset($_GET["RecordingUrl"]) && $_GET["RecordingUrl"]) {
if (file_exists(SAVE_PATH."PHPAPI/".$actual_name.".".$extension)) {
$actual_name = find_new_name($original_name, $extension);
}
else {
$actual_name = $original_name;
}
$name = $actual_name.".".$extension;
file_put_contents(SAVE_PATH.'PHPAPI/'.$name, file_get_contents($_GET["RecordingUrl"]));
}
function find_new_name ( $file, $extension ) {
$name = $file.".".$extension;
$i = 0;
while(file_exists(SAVE_PATH."PHPAPI/".$name)){
$new_name = $file.$i;
$name = $new_name.".".$extension;
$i++;
}
return $new_name;
}
?>
What I changed:
Defined a const SAVE_PATH = "/var/www/osbs/";
Use the new constant everywhere. No more relative sometimes and absolute sometimes, it's all absolute.
Used the constant in file_put_contents (THIS IS THE ACTUAL FIX, YOU NEED A FULL PATH HERE)
Added an additional check to make sure RecordingUrl isset, otherwise you get a PHP warning when it's not set.
The problem seems to be in the first line of your script:
$actual_name = pathinfo("PHPAPI/recording.mp3", PATHINFO_FILENAME);
This will assign recording.mp3 to $actual_filename. You are then checking for recording.mp3.mp3 by concatenating the extension to the filename. I think you want to use PATHINFO_BASENAME which will return the filename sans extension.
Are you sure about the path? /PHPAPI looks for the file inside /PHPAPI, not the expected /var/www/osbs/PHPAPI/. You should check for PHPAPI/$filename instead.
You have to put your all logical in the find_new_name() function. That would made your code clearer
if ($_GET["RecordingUrl"]) {
$name = find_new_name("PHPAPI/recording.mp3");
file_put_contents($name, file_get_contents($_GET["RecordingUrl"]));
}
function find_new_name($name) {
$info = pathinfo($name);
$name = $info['basename'];
$i = 0;
while (file_exists("$info[dirname]/$name")) {
$name = sprintf('%s%d.%s', $info['filename'], ++$i, $info['extension']);
}
return "$info[dirname]/$name";
}
You forgot the path with the file_put_contents().
It should be:
file_put_contents("PHPAPI/".$name, file_get_contents($_GET["RecordingUrl"]));
Or:
file_put_contents("/var/www/osbs/PHPAPI/".$name, file_get_contents($_GET["RecordingUrl"]));
You are confused between URL of a file and its PATH
Your httdoc(or public_html ) root is /var/www/osbs/PHPAPI
But Your filesystem root is '/'
Try
file_put_contents( __DIR__.'/'.$name, file_get_contents($_GET["RecordingUrl"]));
there are lots of bad practices in your code
'file_exists' and a few other file calls like fstat are cached by php. This is documented in the manual for file_exists. Your first call when the file does not exists is saved and returned in subsequent calls. Use 'clearstatcache()' between calls to clear the cache.
A "little" refactoring:
Absolute path everywhere
Transparent function, more self-explanatory name, simpler use of argument
Protection against malicious input ($_POST really doesn't cut it)
Why file_put_contents() when you want to actually copy()
<?php
define("SRC_PATH", "/var/www/osbs/whereverYourSrcIs/");
define("SAVE_PATH", "/var/www/osbs/PHPAPI/");
function findAvailableName($name) {
$i = 1;
$pathinfo = pathinfo($name);
while(file_exists($name)) {
$name = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . "." . $i++ . "." . $pathinfo['extension'];
}
return $name;
}
if (isset($_GET["RecordingUrl"]) && $_GET["RecordingUrl"]) {
if (strpos('/' . $_GET['RecordingUrl'] . '/', '/../') !== false) {
die("invalid input, don't be evil");
}
copy(SRC_PATH . $_GET["RecordingUrl"], findAvailableName(SAVE_PATH . "recording.mp3"));
}

How to auto delete a zip file after using stream_get_contents(); to view a file in php?

I have this code to read a file for preview, but the downside is I have to download the file first from cloud and read from it, but it's a waste of space so I want to delete it after viewing a certain file. Is there an automatic way of doing this? Or do I have to integrate it to a close button?
// Get the container we want to use
$container = $conn->get_container('mailtemplate');
//$filename = 'template1.zip';
// upload file to Rackspace
$object = $container->get_object($filename);
//var_dump($object);
//echo '<pre>' . print_r($object,true) . '</pre>';
$localfile = $dir.$filename;
//echo $localfile;
$object->save_to_filename($localfile);
if($_GET['preview'] == "true")
{
$dir = "../mailtemplates/";
$file1 = $_GET['tfilename'];
$file = $dir.$file1;
$file2 = "index.html";
$info = pathinfo($file);
$file_name = basename($file,'.'.$info['extension']);
$path = $file_name.'/'.$file2;
$zip = new ZipArchive();
$zip->open($file);
$fp = $zip->getStream($path);
if(!$fp)
{
exit("faileds\n");
$zip->close();
unlink($dir.$filename);
}
else
{
$stuff = stream_get_contents($fp);
echo $stuff;
$zip->close();
if($stuff != null)
{
unlink($dir.$filename);
}
}
}
else
{
unlink($dir.$filename);
}
You didn't google this did ya?
Try Unlink
Edit:
Taking a look at this code, $zip->open($file); <-- is where you open the file. The file variable is set by:
"../mailtemplates/" . basename($_GET['tfilename'], '.' . $info['extension']) . '/' . "index.html"
So you're grabbing a relative directory and grabbing a filename as a folder, and going to that folder /index.html. Here's an example:
if you're in c:\ testing and you go to ../mailtemplates/ you'll be in c:\mailtemplates and then you're looking at file test.php but you're removing the file extension, so you'll be opening the location c:\mailtemplates\test\index.html so you open up that html file and read it. Then, you're trying to delete c:\mailtemplates\test.php
can you explain how any of that makes sense to you? 'cause that seems very odd to me.

PHP check file exists without knowing the extension

I need to check if a file exists but I don't know the extension.
IE I would like to do:
if(file_exists('./uploads/filename')):
// do something
endif;
Of course that wont work as it has no extension. the extension will be either jpg, jpeg, png, gif
Any ideas of a way of doing this without doing a loop ?
You would have to do a glob():
$result = glob ("./uploads/filename.*");
and see whether $result contains anything.
I've got the same need, and tried to use glob but this function seems to not be portable :
See notes from http://php.net/manual/en/function.glob.php :
Note: This function isn't available on some systems (e.g. old Sun OS).
Note: The GLOB_BRACE flag is not available on some non GNU systems, like Solaris.
It also more slower than opendir, take a look at : Which is faster: glob() or opendir()
So I've made a snippet function that does the same thing :
function resolve($name) {
// reads informations over the path
$info = pathinfo($name);
if (!empty($info['extension'])) {
// if the file already contains an extension returns it
return $name;
}
$filename = $info['filename'];
$len = strlen($filename);
// open the folder
$dh = opendir($info['dirname']);
if (!$dh) {
return false;
}
// scan each file in the folder
while (($file = readdir($dh)) !== false) {
if (strncmp($file, $filename, $len) === 0) {
if (strlen($name) > $len) {
// if name contains a directory part
$name = substr($name, 0, strlen($name) - $len) . $file;
} else {
// if the name is at the path root
$name = $file;
}
closedir($dh);
return $name;
}
}
// file not found
closedir($dh);
return false;
}
Usage :
$file = resolve('/var/www/my-website/index');
echo $file; // will output /var/www/my-website/index.html (for example)
Hope that could helps someone,
Ioan

How to remove extension from string (only real extension!)

I'm looking for a small function that allows me to remove the extension from a filename.
I've found many examples by googling, but they are bad, because they just remove part of the string with "." . They use dot for limiter and just cut string.
Look at these scripts,
$from = preg_replace('/\.[^.]+$/','',$from);
or
$from=substr($from, 0, (strlen ($from)) - (strlen (strrchr($filename,'.'))));
When we add the string like this:
This.is example of somestring
It will return only "This"...
The extension can have 3 or 4 characters, so we have to check if dot is on 4 or 5 position, and then remove it.
How can it be done?
http://php.net/manual/en/function.pathinfo.php
pathinfo — Returns information about a file path
$filename = pathinfo('filename.md.txt', PATHINFO_FILENAME); // returns 'filename.md'
Try this one:
$withoutExt = preg_replace('/\\.[^.\\s]{3,4}$/', '', $filename);
So, this matches a dot followed by three or four characters which are not a dot or a space. The "3 or 4" rule should probably be relaxed, since there are plenty of file extensions which are shorter or longer.
From the manual, pathinfo:
<?php
$path_parts = pathinfo('/www/htdocs/index.html');
echo $path_parts['dirname'], "\n";
echo $path_parts['basename'], "\n";
echo $path_parts['extension'], "\n";
echo $path_parts['filename'], "\n"; // Since PHP 5.2.0
?>
It doesn't have to be a complete path to operate properly. It will just as happily parse file.jpg as /path/to/my/file.jpg.
Use PHP basename()
(PHP 4, PHP 5)
var_dump(basename('test.php', '.php'));
Outputs: string(4) "test"
This is a rather easy solution and will work no matter how long the extension or how many dots or other characters are in the string.
$filename = "abc.def.jpg";
$newFileName = substr($filename, 0 , (strrpos($filename, ".")));
//$newFileName will now be abc.def
Basically this just looks for the last occurrence of . and then uses substring to retrieve all the characters up to that point.
It's similar to one of your googled examples but simpler, faster and easier than regular expressions and the other examples. Well imo anyway. Hope it helps someone.
Recommend use: pathinfo with PATHINFO_FILENAME
$filename = 'abc_123_filename.html';
$without_extension = pathinfo($filename, PATHINFO_FILENAME);
You could use what PHP has built in to assist...
$withoutExt = pathinfo($path, PATHINFO_DIRNAME) . '/' . pathinfo($path, PATHINFO_FILENAME);
Though if you are only dealing with a filename (.somefile.jpg), you will get...
./somefile
See it on CodePad.org
Or use a regex...
$withoutExt = preg_replace('/\.' . preg_quote(pathinfo($path, PATHINFO_EXTENSION), '/') . '$/', '', $path);
See it on CodePad.org
If you don't have a path, but just a filename, this will work and be much terser...
$withoutExt = pathinfo($path, PATHINFO_FILENAME);
See it on CodePad.org
Of course, these both just look for the last period (.).
The following code works well for me, and it's pretty short. It just breaks the file up into an array delimited by dots, deletes the last element (which is hypothetically the extension), and reforms the array with the dots again.
$filebroken = explode( '.', $filename);
$extension = array_pop($filebroken);
$fileTypeless = implode('.', $filebroken);
I found many examples on the Google but there are bad because just remove part of string with "."
Actually that is absolutely the correct thing to do. Go ahead and use that.
The file extension is everything after the last dot, and there is no requirement for a file extension to be any particular number of characters. Even talking only about Windows, it already comes with file extensions that don't fit 3-4 characters, such as eg. .manifest.
There are a few ways to do it, but i think one of the quicker ways is the following
// $filename has the file name you have under the picture
$temp = explode( '.', $filename );
$ext = array_pop( $temp );
$name = implode( '.', $temp );
Another solution is this. I havent tested it, but it looks like it should work for multiple periods in a filename
$name = substr($filename, 0, (strlen ($filename)) - (strlen (strrchr($filename,'.'))));
Also:
$info = pathinfo( $filename );
$name = $info['filename'];
$ext = $info['extension'];
// Or in PHP 5.4, i believe this should work
$name = pathinfo( $filename )[ 'filename' ];
In all of these, $name contains the filename without the extension
$image_name = "this-is.file.name.jpg";
$last_dot_index = strrpos($image_name, ".");
$without_extention = substr($image_name, 0, $last_dot_index);
Output:
this-is.file.name
As others mention, the idea of limiting extension to a certain number of characters is invalid. Going with the idea of array_pop, thinking of a delimited string as an array, this function has been useful to me...
function string_pop($string, $delimiter){
$a = explode($delimiter, $string);
array_pop($a);
return implode($delimiter, $a);
}
Usage:
$filename = "pic.of.my.house.jpeg";
$name = string_pop($filename, '.');
echo $name;
Outputs:
pic.of.my.house (note it leaves valid, non-extension "." characters alone)
In action:
http://sandbox.onlinephpfunctions.com/code/5d12a96ea548f696bd097e2986b22de7628314a0
This works when there is multiple parts to an extension and is both short and efficient:
function removeExt($path)
{
$basename = basename($path);
return strpos($basename, '.') === false ? $path : substr($path, 0, - strlen($basename) + strlen(explode('.', $basename)[0]));
}
echo removeExt('https://example.com/file.php');
// https://example.com/file
echo removeExt('https://example.com/file.tar.gz');
// https://example.com/file
echo removeExt('file.tar.gz');
// file
echo removeExt('file');
// file
You can set the length of the regular expression pattern by using the {x,y} operator. {3,4} would match if the preceeding pattern occurs 3 or 4 times.
But I don't think you really need it. What will you do with a file named "This.is"?
Landed on this page for looking for the fastest way to remove the extension from a number file names from a glob() result.
So I did some very rudimentary benchmark tests and found this was the quickest method. It was less than half the time of preg_replace():
$result = substr($fileName,0,-4);
Now I know that all of the files in my glob() have a .zip extension, so I could do this.
If the file extension is unknown with an unknown length, the following method will work and is still about 20% faster that preg_replace(). That is, so long as there is an extension.
$result = substr($fileName,0,strrpos($fileName,'.'));
The basic benchmark test code and the results:
$start = microtime(true);
$loop = 10000000;
$fileName = 'a.LONG-filename_forTest.zip';
$result;
// 1.82sec preg_replace() unknown ext
//do {
// $result = preg_replace('/\\.[^.\\s]{3,4}$/','',$fileName);
//} while(--$loop);
// 1.7sec preg_replace() known ext
//do {
// $result = preg_replace('/.zip$/','',$fileName);
//} while(--$loop);
// 4.57sec! - pathinfo
//do {
// $result = pathinfo($fileName,PATHINFO_FILENAME);
//} while(--$loop);
// 2.43sec explode and implode
//do {
// $result = implode('.',explode('.',$fileName,-1));
//} while(--$loop);
// 3.74sec basename, known ext
//do {
// $result = basename($fileName,'.zip');
//} while(--$loop);
// 1.45sec strpos unknown ext
//do {
// $result = substr($fileName,0,strrpos($fileName,'.'));
//} while(--$loop);
// 0.73sec strpos - known ext length
do {
$result = substr($fileName,0,-4);
} while(--$loop);
var_dump($fileName);
var_dump($result);
echo 'Time:['.(microtime(true) - $start).']';
exit;
Use this:
strstr('filename.ext','.',true);
//result filename
Try to use this one. it will surely remove the file extension.
$filename = "image.jpg";
$e = explode(".", $filename);
foreach($e as $key=>$d)
{
if($d!=end($e)
{
$new_d[]=$d;
}
}
echo implode("-",$new_t); // result would be just the 'image'
EDIT:
The smartest approach IMHO, it removes the last point and following text from a filename (aka the extension):
$name = basename($filename, '.' . end(explode('.', $filename)));
Cheers ;)

Categories