php how to detect the extension of a file - php

I'm using curl to download different files (e.g .doc, .jpg, jpeg , docx etc) from a webmail so I would like to know how find this extension because the link that I'm using(e.g example.com/file=3838438) basically outputs the file content but there is no file extension specified in the link.

You probably want the MIME types, not the extension (which is unreliable). The MIME type is a portable way to identify the file type. Examples are image/jpeg and text/html.
First, check whether that site tells you the mime type in the HTTP response. You want to look for the Content-Type header.
If that isn't useful, you can use finfo_file or finfo_buffer to guess mime types. These are available from PECL, or in PHP 5.3 and later.
In older versions of PHP, you can use mime_content_type.

If you're in a Un*x hosted environment, you can call exec('file ' . $file_path, $result) to have the system do an analysis of the file. Check $result for it's answer.
It will have something like:
Filename: ASCII text
Filename: PDF document, version 1.3
etc.

If you don't have to use curl, here is how you could do it using straight PHP.
Note: It is possible to set headers, credentials etc. while using fopen() to fetch URL's using the HTTP context options.
<?php
class DownloadWithExtension
{
// array for caching mimetypes
public $mimetypes = array();
// create mimetype cache when instantiated
public function __construct() {
// iterate through /etc/mime.types and create a hash with
// mime-types as keys and the first extension as the value.
foreach(preg_grep('/(^#)|(^\s+$)/',
file('/etc/mime.types'), PREG_GREP_INVERT) as $line) {
$minfo = preg_split('/\t+/', $line);
if (count($minfo) > 1) {
$this->mimetypes[$minfo[0]] = trim(array_shift(explode(' ', $minfo[1])));
}
}
}
// download $url and save as $prefix while automatically
// determining appropriate extension.
// #param $url - URL to download
// #param $prefix - Filename to save to (without extension)
// #return filename used
public function get($url, $prefix) {
$mimetype = NULL;
$filename = NULL;
$src = fopen($url, 'r');
if (! $src) {
throw new Exception('Failed to open: ' . $url);
}
$meta = stream_get_meta_data($src);
foreach($meta['wrapper_data'] as $header){
if (preg_match('/^content-type: ([^\s]+)/i', $header, &$matches)) {
$mimetype = $matches[1];
break;
}
}
$extension = #$this->mimetypes[$mimetype];
// default to .bin if the mime-type could not be determined
$filename = sprintf('%s.%s', $prefix, $extension ? $extension : 'bin');
$dst = fopen($filename, 'w');
if (! ($dst && stream_copy_to_stream($src, $dst) &&
fclose($src) && fclose($dst))) {
throw new Exception('An error occurred while saving the file!');
}
return $filename;
}
}
$d = new DownloadWithExtension();
$url = 'http://example.com?file=3838438';
$filename = $d->get($url, '/tmp/myfile');
print(sprintf("Saved %s as %s\n", $url, $filename));

Related

Getting MimeType from file source in PHP [duplicate]

I'm using PHP to send an email with an attachment. The attachment could be any of several different file types (pdf, txt, doc, swf, etc).
First, the script gets the file using "file_get_contents".
Later, the script echoes in the header:
Content-Type: <?php echo $the_content_type; ?>; name="<?php echo $the_file_name; ?>"
How to I set the correct value for $the_content_type?
I am using this function, which includes several fallbacks to compensate for older versions of PHP or simply bad results:
function getFileMimeType($file) {
if (function_exists('finfo_file')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, $file);
finfo_close($finfo);
} else {
require_once 'upgradephp/ext/mime.php';
$type = mime_content_type($file);
}
if (!$type || in_array($type, array('application/octet-stream', 'text/plain'))) {
$secondOpinion = exec('file -b --mime-type ' . escapeshellarg($file), $foo, $returnCode);
if ($returnCode === 0 && $secondOpinion) {
$type = $secondOpinion;
}
}
if (!$type || in_array($type, array('application/octet-stream', 'text/plain'))) {
require_once 'upgradephp/ext/mime.php';
$exifImageType = exif_imagetype($file);
if ($exifImageType !== false) {
$type = image_type_to_mime_type($exifImageType);
}
}
return $type;
}
It tries to use the newer PHP finfo functions. If those aren't available, it uses the mime_content_type alternative and includes the drop-in replacement from the Upgrade.php library to make sure this exists. If those didn't return anything useful, it'll try the OS' file command. AFAIK that's only available on *NIX systems, you may want to change that or get rid of it if you plan to use this on Windows. If nothing worked, it tries exif_imagetype as fallback for images only.
I have come to notice that different servers vary widely in their support for the mime type functions, and that the Upgrade.php mime_content_type replacement is far from perfect. The limited exif_imagetype functions, both the original and the Upgrade.php replacement, are working pretty reliably though. If you're only concerned about images, you may only want to use this last one.
It very easy to have it in php.
Simply call the following php function mime_content_type
<?php
$filelink= 'uploads/some_file.pdf';
$the_content_type = "";
// check if the file exist before
if(is_file($file_link)) {
$the_content_type = mime_content_type($file_link);
}
// You can now use it here.
?>
PHP documentation of the function mime_content_type()
Hope it helps someone
With finfo_file: http://us2.php.net/manual/en/function.finfo-file.php
Here's an example using finfo_open which is available in PHP5 and PECL:
$mimepath='/usr/share/magic'; // may differ depending on your machine
// try /usr/share/file/magic if it doesn't work
$mime = finfo_open(FILEINFO_MIME,$mimepath);
if ($mime===FALSE) {
throw new Exception('Unable to open finfo');
}
$filetype = finfo_file($mime,$tmpFileName);
finfo_close($mime);
if ($filetype===FALSE) {
throw new Exception('Unable to recognise filetype');
}
Alternatively, you can use the deprecated mime_ content_ type function:
$filetype=mime_content_type($tmpFileName);
or use the OS's in built functions:
ob_start();
system('/usr/bin/file -i -b ' . realpath($tmpFileName));
$type = ob_get_clean();
$parts = explode(';', $type);
$filetype=trim($parts[0]);
function getMimeType( $filename ) {
$realpath = realpath( $filename );
if ( $realpath
&& function_exists( 'finfo_file' )
&& function_exists( 'finfo_open' )
&& defined( 'FILEINFO_MIME_TYPE' )
) {
// Use the Fileinfo PECL extension (PHP 5.3+)
return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath );
}
if ( function_exists( 'mime_content_type' ) ) {
// Deprecated in PHP 5.3
return mime_content_type( $realpath );
}
return false;
}
This worked for me
Why is mime_content_type() deprecated in PHP?
I guess that i found a short way.
Get the image size using:
$infFil=getimagesize($the_file_name);
and
Content-Type: <?php echo $infFil["mime"] ?>; name="<?php echo $the_file_name; ?>"
The getimagesize returns an associative array which have a MIME key
I used it and it works
I've tried most of the suggestions, but they all fail for me (I'm inbetween any usefull version of PHP apparantly. I ended up with the following function:
function getShellFileMimetype($file) {
$type = shell_exec('file -i -b '. escapeshellcmd( realpath($_SERVER['DOCUMENT_ROOT'].$file)) );
if( strpos($type, ";")!==false ){
$type = current(explode(";", $type));
}
return $type;
}
From string: $mediaType = (new \finfo(FILEINFO_MIME))->buffer($string)
From filename: $mediaType = (new \finfo(FILEINFO_MIME))->file($filename)
The ext-fileinfo module is required (usually it is preinstalled) for PHP.
FILEINFO_MIME - Return the mime type and mime encoding as defined by RFC 2045..
If you use composer, don't forget. to add the ext-fileinfo entry to composer.json
Documentation: https://www.php.net/manual/en/ref.fileinfo.php
I really recommend using a Framework like "CodeIgniter" for seinding Emails. Here is a Screencast about "Sending Emails with CodeIgniter" in only 18 Minutes.
http://net.tutsplus.com/videos/screencasts/codeigniter-from-scratch-day-3/
try this:
function ftype($f) {
curl_setopt_array(($c = #curl_init((!preg_match("/[a-z]+:\/{2}(?:www\.)?/i",$f) ? sprintf("%s://%s/%s", "http" , $_SERVER['HTTP_HOST'],$f) : $f))), array(CURLOPT_RETURNTRANSFER => 1, CURLOPT_HEADER => 1));
return(preg_match("/Type:\s*(?<mime_type>[^\n]+)/i", #curl_exec($c), $m) && curl_getinfo($c, CURLINFO_HTTP_CODE) != 404) ? ($m["mime_type"]) : 0;
}
echo ftype("http://img2.orkut.com/images/medium/1283204135/604747203/ln.jpg"); // print image/jpeg

Get extension of a image and append it to filename and send headers accordingly

I have a file picviwer.php that loads a pic as following:
<img src="http://www.example.com/loadimage?uid=$id&view=pic" id="ppic" />
It sends a GET request to a another file loadimage.php with the id of the photo to be loaded.
Below is the code for loadimage.php
if(isset($_GET['uid'])){
$uid = $_GET['uid'];
$remoteImage = "http://www.example.com/user-pics/".$uid.".png";
$img = file_get_contents($remoteImage);
header('Content-Type: image/x-png'); //or whatever
readfile($remoteImage);
}
Right now the above code deals only for png image and I wish to extend for images of all types(.jpg,.gif etc.). For this I want to get the extension of the image and then send headers accordingly. Also append the correct extension with the filename(uid).How can I do that?
i hope it will help you . it would be little long but surly will work and can add more extension in else if condtion .
if(file_exists("http://www.example.com/user-pics/".$uid.".png")) {
$remoteImage = "http://www.example.com/user-pics/".$uid.".png";
}elseif( file_exists("http://www.example.com/user-pics/".$uid.".jpg") ){
$remoteImage = "http://www.example.com/user-pics/".$uid.".jpg";
}
and so on
I would recommend not accessing the files via the full domain name but by the path they are on your server. This would eliminate the server load it takes to form a internal HTTP request to check the file existence and read the content.
You could find the exiting extensions as follows:
if(isset($_GET['uid'])){
$uid = $_GET['uid'];
$imagesPath = '/path/to/images/'; //REPLACE with the correct server path
$existingImage = '';
foreach (glob($imagesPath . $uid . ".*") as $filename) {
// this matches all files with name $uid and an existing extension. If you have preferred extensions handle them here.
$existingImage = $filename;
break; // We only need one if we have no extension preference
}
if ('' === $existingImage) {
// No images are found that have the required filename. Handle this exception here
}
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype
$imgMimeType = finfo_file($finfo, $existingImage);
finfo_close($finfo);
header('Content-Type: ' . $imgMimeType);
readfile($existingImage );
}
You can grab the extension for a file using:
$extension = pathinfo($imagePath, PATHINFO_EXTENSION);
To get the correct mime-type for the header use exif_imagetype() to get the imagetype which can be converted to the correct mime-type.
(provided you will only use images).
$imageType = exif_imagetype($imagePath);
$headerString = 'Content-Type:'. image_type_to_mime_type ($imageType);
header($headerString);

How do you create a .gz file using PHP?

I would like to gzip compress a file on my server using PHP. Does anyone have an example that would input a file and output a compressed file?
This code does the trick
// Name of the file we're compressing
$file = "test.txt";
// Name of the gz file we're creating
$gzfile = "test.gz";
// Open the gz file (w9 is the highest compression)
$fp = gzopen ($gzfile, 'w9');
// Compress the file
gzwrite ($fp, file_get_contents($file));
// Close the gz file and we're done
gzclose($fp);
The other answers here load the entire file into memory during compression, which will cause 'out of memory' errors on large files. The function below should be more reliable on large files as it reads and writes files in 512kb chunks.
/**
* GZIPs a file on disk (appending .gz to the name)
*
* From http://stackoverflow.com/questions/6073397/how-do-you-create-a-gz-file-using-php
* Based on function by Kioob at:
* http://www.php.net/manual/en/function.gzwrite.php#34955
*
* #param string $source Path to file that should be compressed
* #param integer $level GZIP compression level (default: 9)
* #return string New filename (with .gz appended) if success, or false if operation fails
*/
function gzCompressFile($source, $level = 9){
$dest = $source . '.gz';
$mode = 'wb' . $level;
$error = false;
if ($fp_out = gzopen($dest, $mode)) {
if ($fp_in = fopen($source,'rb')) {
while (!feof($fp_in))
gzwrite($fp_out, fread($fp_in, 1024 * 512));
fclose($fp_in);
} else {
$error = true;
}
gzclose($fp_out);
} else {
$error = true;
}
if ($error)
return false;
else
return $dest;
}
UPDATE: Gerben has posted an improved version of this function that is cleaner and uses exceptions instead of returning false on an error. See https://stackoverflow.com/a/56140427/195835
Also, you could use php's wrappers, the compression ones. With a minimal change in the code you would be able to switch between gzip, bzip2 or zip.
$input = "test.txt";
$output = $input.".gz";
file_put_contents("compress.zlib://$output", file_get_contents($input));
change compress.zlib:// to compress.zip:// for zip compression (see comment to this answer about zip compression), or to compress.bzip2:// to bzip2 compression.
Simple one liner with gzencode():
gzencode(file_get_contents($file_name));
If you are looking to just unzip a file, this works and doesn't cause issues with memory:
$bytes = file_put_contents($destination, gzopen($gzip_path, r));
It's probably obvious to many, but if any of the program execution functions is enabled on your system (exec, system, shell_exec), you can use them to simply gzip the file.
exec("gzip ".$filename);
N.B.: Be sure to properly sanitize the $filename variable before using it, especially if it comes from user input (but not only). It may be used to run arbitrary commands, for example by containing something like my-file.txt && anothercommand (or my-file.txt; anothercommand).
Here's an improved version. I got rid of all the nested if/else statements, resulting in lower cyclomatic complexity, there's better error handling through exceptions instead of keeping track of a boolean error state, some type hinting and I'm bailing out if the file has a gz extension already. It got a little longer in terms of lines of code, but it's much more readable.
/**
* Compress a file using gzip
*
* Rewritten from Simon East's version here:
* https://stackoverflow.com/a/22754032/3499843
*
* #param string $inFilename Input filename
* #param int $level Compression level (default: 9)
*
* #throws Exception if the input or output file can not be opened
*
* #return string Output filename
*/
function gzcompressfile(string $inFilename, int $level = 9): string
{
// Is the file gzipped already?
$extension = pathinfo($inFilename, PATHINFO_EXTENSION);
if ($extension == "gz") {
return $inFilename;
}
// Open input file
$inFile = fopen($inFilename, "rb");
if ($inFile === false) {
throw new \Exception("Unable to open input file: $inFilename");
}
// Open output file
$gzFilename = $inFilename.".gz";
$mode = "wb".$level;
$gzFile = gzopen($gzFilename, $mode);
if ($gzFile === false) {
fclose($inFile);
throw new \Exception("Unable to open output file: $gzFilename");
}
// Stream copy
$length = 512 * 1024; // 512 kB
while (!feof($inFile)) {
gzwrite($gzFile, fread($inFile, $length));
}
// Close files
fclose($inFile);
gzclose($gzFile);
// Return the new filename
return $gzFilename;
}
Compress folder for anyone needs
function gzCompressFile($source, $level = 9)
{
$tarFile = $source . '.tar';
if (is_dir($source)) {
$tar = new PharData($tarFile);
$files = scandir($source);
foreach ($files as $file) {
if (is_file($source . '/' . $file)) {
$tar->addFile($source . '/' . $file, $file);
}
}
}
$dest = $tarFile . '.gz';
$mode = 'wb' . $level;
$error = false;
if ($fp_out = gzopen($dest, $mode)) {
if ($fp_in = fopen($tarFile, 'rb')) {
while (!feof($fp_in))
gzwrite($fp_out, fread($fp_in, 1024 * 512));
fclose($fp_in);
} else {
$error = true;
}
gzclose($fp_out);
unlink($tarFile);
} else {
$error = true;
}
if ($error)
return false;
else
return $dest;
}
copy('file.txt', 'compress.zlib://' . 'file.txt.gz');
See documentation

How can I get the mime type in PHP

I am writing a script and I need to correctly (I think some mime types can be different from their extensions) get the mime types of files (the files can be of any type).
Web hosting company in use does not have mime_content_type() and is still (don't know which year they will be fixing it) promising to fix the PECL alternative.
Which other way can I go about it (and I don;t have access to shell commands)?
You could try finfo_file - it returns the mime type.
http://php.net/manual/en/book.fileinfo.php
I use the following function, which is a wrapper for the 3 most common methods:
function Mime($path, $magic = null)
{
$path = realpath($path);
if ($path !== false)
{
if (function_exists('finfo_open') === true)
{
$finfo = finfo_open(FILEINFO_MIME_TYPE, $magic);
if (is_resource($finfo) === true)
{
$result = finfo_file($finfo, $path);
}
finfo_close($finfo);
}
else if (function_exists('mime_content_type') === true)
{
$result = mime_content_type($path);
}
else if (function_exists('exif_imagetype') === true)
{
$result = image_type_to_mime_type(exif_imagetype($path));
}
return preg_replace('~^(.+);.+$~', '$1', $result);
}
return false;
}
$_FILES['file']['type'] comes from the browser that uploads the file so you can't rely on this value at all.
Check out finfo_file for identifying file types based on file content. The extension of the file is also unreliable as the user could upload malicious code with an mp3 extension.

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

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'

Categories