I am trying to display an image from a PHP server.
Here it is my function:
function get_file($path) {
$fileToGet = $GLOBALS['homedir'].$path;
//echo $fileToGet.PHP_EOL;
if (file_exists($fileToGet)) {
//echo 'file exists';
header('Content-Type: image/png');
header('Content-Length: '.filesize($fileToGet));
echo file_get_contents($file);
}
}
I am using the browser or postman and the image is invalid.
What I am missing?
I edited a little bit your function:
function get_file( $path ){
$fileToGet = $GLOBALS['homedir'];
if( substr( $fileToGet, -1) != '/' ){
// add trailing slash if needed
$fileToGet .= '/';
}
$fileToGet .= $path;
if (file_exists($fileToGet)) {
header('Content-Type: image/png');
header('Content-Length: '.filesize($fileToGet));
echo file_get_contents($fileToGet);
}
}
Just a security hint: if $path comes from the user there may be a problem because he will be able to access to some other file.
Think about this code:
get_file( $_GET['path'] );
then the user can call this url
yoursite/yourpage.php?path=../../../mypreciousimage.png
You're not outputting the contents of the file you're reading:
file_get_contents($file);
You'll need to echo it:
echo file_get_contents($file);
Or:
readfile($file);
You'll probably also want to add exit; to the end of that function, to ensure that no other code runs and that no other output gets sent.
Try
print file_get_contents($file);
Instead of
file_get_contents($file);
Related
I'm trying to open SVG file first in PHP and then return this data:
$file = dirname(__FILE__) . $_GET["file"] . ".svg";
if (!file_exists($file)) {
$file = dirname(__FILE__) . $_GET["file"] . ".png";
if (!file_exists($file)) {
throw new NotFoundHttpException();
} else
header('Content-Type: image/png');
} else
header('Content-Type: image/svg+xml');
$content = file_get_contents($file);
return $content;
And in HTML:
<img src="script.php?file=someimage">
Problem is that its not showing SVG images in the tag. It works, if I set script.php?file=someimage to the URL string of my browser, but not inside the tag. PNG works fine. If i set just
<img src="someimage.svg">
it also works perfect.
embed and object tags works, but I need img.
UPDATE:
The problem was in Yii2, I send headers wrong way. In some reason it works for PNG, but not for SVG.
It should be done like that:
Yii::$app->response->format = \yii\web\Response::FORMAT_RAW;
Yii::$app->response->headers->add('Content-Type', 'image/svg+xml');
Looks like you forgot the /. Try replacing these lines where appropriate:
$file = dirname(__FILE__) . '/' . $_GET["file"] . ".svg";
// ...
$file = dirname(__FILE__) '/' . $_GET["file"] . ".png";
dirname() gives back a string without a trailing slash, so it was trying to open /path/to/scriptdirsomefile.svg.
Security
Also, I noticed that your script can be made to do bad things, such as exposing arbitrary files on the server. You can prevent a lot of exploits by doing some basic sanitizing of $_GET['file'] before starting:
$_GET['file'] = preg_replace('#/#', '_', $_GET['file']);
Change return $content; to echo $content;
So if the image files are on the same directory, it will be:
<?php
$file = $_GET["file"] . ".svg";
if (!file_exists($file)) {
$file = $_GET["file"] . ".png";
if (!file_exists($file)) {
throw new NotFoundHttpException();
} else
header('Content-Type: image/png');
} else
header('Content-Type: image/svg+xml');
$content = file_get_contents($file);
echo $content;
?>
Working example #1 (showing svg):
http://www.createchhk.com/SOanswers/subc/index.php
Working example #2 (showing png):
http://www.createchhk.com/SOanswers/subc/index2.php
This method receives a foldername and filename. It should make sure that the filename actually really exists in the folder path before doing anything. If so then it can proceed to get the file, send headers and send the response content. Is there a better and more secure way to to do this?
public function is_file_in_path($filename, $filepath)
{
if (file_exists($filepath)) {
$filename = realpath($filepath);
}
if ($filename) {
$image_mime = get_mime_by_extension($filename);
header('Content-Type: ' . $image_mime);
header('Content-Length: ' . filesize($filename));
echo file_get_contents($filename);
} else {
show_error('No image found.', 404);
}
}
To make the function to have higher compatibility and cleaner:
consider removing the word "public"
remove the 1st parameter, which is a bit redundant
change get_mime_by_extension to mime_content_type
change "No image found" to "No File found" (because this function can also be used if you supply a PDF file as parameter).
<?php
function is_file_in_path($filepath)
{
if (file_exists($filepath)) {
$filename = realpath($filepath);
}
if ($filename) {
$file_mime = mime_content_type($filename);
header('Content-Type: ' . $file_mime);
header('Content-Length: ' . filesize($filename));
echo file_get_contents($filename);
} else {
// show_error('No image found.', 404);
echo "No File Found";
}}
// is_file_in_path("./photo.jpg");
is_file_in_path("./ess.pdf");
?>
I'm having some trouble with this one. I have found some helpful scripts on the web and have been modifying them for my needs. However, I can't seem to download a file. It will respond back with the contents of the file but doesn't download it. I am using Polymer 1.0+ for my client side and PHP for my server side. The client side code to download a file is as follows:
<!--THIS IS THE HTML SIDE-->
<iron-ajax
id="ajaxDownloadItem"
url="../../../dropFilesBackend/index.php/main/DownloadItem"
method="GET"
handle-as="document"
last-response="{{downloadResponse}}"
on-response="ajaxDownloadItemResponse">
</iron-ajax>
//THIS IS THE JAVASCRIPT THAT WILL CALL THE "iron-ajax" ELEMENT
downloadItem:function(e){
this.$.ajaxDownloadItem.params = {"FILENAME":this.selectedItem.FILENAME,
"PATH":this.folder};
this.$.ajaxDownloadItem.generateRequest();
},
The server side code is as follows (the url is different because I do some url modification to get to the correct script):
function actionDownloadItem(){
valRequestMethodGet();
$username = $_SESSION['USERNAME'];
if(validateLoggedIn($username)){
$itemName = arrayGet($_GET,"FILENAME");
$path = arrayGet($_GET,"PATH");
$username = $_SESSION['USERNAME'];
$downloadItem = CoreFilePath();
$downloadItem .= "/".$_SESSION['USERNAME']."".$path."".$itemName;
DownloadFile($downloadItem);
}
else {
echo "Not Logged In.";
}
}
function DownloadFile($filePath) {
//ignore_user_abort(true);
set_time_limit(0); // disable the time limit for this script
//touch($filePath);
//chmod($filePath, 0775);
if ($fd = fopen($filePath, "r")) {
$fsize = filesize($filePath);//this returns 12
$path_parts = pathinfo($filePath);//basename = textfile.txt
$ext = strtolower($path_parts["extension"]);//this returns txt
$header = headerMimeType($ext); //this returns text/plain
header('Content-disposition: attachment; filename="'.$path_parts["basename"].'"'); // use 'attachment' to force a file download
header("Content-type: $header");
header("Content-length: $fsize");
header("Cache-control: private"); //use this to open files directly
while(!feof($fd)) {
$buffer = fread($fd, 2048);
echo $buffer;
}
}
fclose ($fd);
}
Any help on this one would be greatly appreciated.
First you will need the file handle
$pathToSave = '/home/something/something.txt';
$writeHandle = fopen($pathToSave, 'wb');
Then, while you are reading the download, write to the file instead of echoing
fwrite($writeHandle, fread($fd, 2048));
Finally, after writing to the file finished close the handle
fclose($writeHandle);
I neglect the error check, you should implement your own.
I am trying to get this code to work but for some reason, all the echo's are able to output correct content, but the headers don't seem to want to force the download of my document. What follows is the file I am trying to build for file downloads. It is set to input code like this: downloader.php?f=13&t=doc to download a file that is named 201-xxx.doc or 201-xxx.pdf from one of two folders depending on the users privileges.
All the logic works up to the header info at the bottom. If I comment out the header content type and the header content disposition, then it will read the file into the browser. With either of those lines included, it give me an error that says "Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found."
<?php
//ob_start();
if ( !defined('__DIR__') ) define('__DIR__', dirname(__FILE__));
define( "TLOJ_FSROOT", __DIR__ . "/" );
define('WP_USE_THEMES', false);
require('./wp-blog-header.php');
$lessonnumber = $_REQUEST['f'];
$type = $_REQUEST['t'];
if ( $lessonnumber < '10' ) { $threedigitlesson = '00' . $lessonnumber; }
elseif ( $lessonnumber < '100' ) { $threedigitlesson = '0' . $lessonnumber; }
else { $threedigitlesson = $lessonnumber; }
$filenamestart = "201-" . $threedigitlesson;
$contenttype = 'application/octet-stream';
switch ($type) {
case 'pdf':
$extension = '.' . $type;
$contenttype = 'application/pdf';
break;
case 'doc':
$extension = '.' . $type;
$contenttype = 'application/msword';
break;
default:
$contenttype = '';
exit("It appears that you are trying to download a file that is not a lesson document. Please contact us if you believe this to be an error.");
}
$filename = $filenamestart . '.' . $type;
$current_user = wp_get_current_user();
//$siteurl = site_url();
$pathroot = TLOJ_FSROOT;
$download_path = $pathroot . "1hoefl4priaspoafr/";
if ( current_user_can("access_s2member_ccap_extendedlessons")) {
$download_path = $download_path . "ex/";
} else {
$download_path = $download_path . "st/";
}
$file_path = $download_path . $filename;
$tlojmemberlength = tlojunlocklessons();
if ( !is_user_logged_in() ) { exit("Please log in to access the file"); }
if ( !current_user_can("access_s2member_ccap_downloadlessons") ) { exit("You don't have access to download the lessons!"); }
if ( $lessonnumber > $tlojmemberlength ) { exit("It appears you are trying to jump ahead! While I am excited at your enthusiam, let's not rush our study time."); }
if ( ($lessonnumber > '195') && (!current_user_can("access_s2member_ccap_lastweek")) ) { exit("Upgrade now to access the downloads for the five bonus lessons!"); }
// build Final File Name
$extendedmessage = "";
if ( current_user_can("access_s2member_ccap_extendedlessons")) { $extendedmessage = " - Extended"; }
$myfinishedlessonname = "Lesson " . $lessonnumber . $extendedmessage . " -- The Life of Jesus Study" . "." . $type;
// echo 'Download Path: ' . $download_path . '<br />';
// echo 'Source/Lesson Number: ' . $lessonnumber . '<br />';
// echo 'File Name: ' . $filename . '<br />';
// echo 'File Type: ' . $type . '<br />';
// echo 'Allowed Lessons: ' . $tlojmemberlength . '<br />';
// echo 'Final File Name: ' . $myfinishedlessonname . '<br />';
// echo 'File Path: ' . $file_path . '<br />';
// echo 'Content Type: ' . $contenttype . '<br />';
// echo 'File Size: ' . filesize($file_path) . '<br />';
if (headers_sent()) { exit("Sorry but the headers have already been sent."); }
ob_end_clean();
if (file_exists($file_path)) {
header('Content-Description: File Transfer');
header('Content-type: ' . $contenttype);
header('Content-disposition: attachment; filename="' . $myfinishedlessonname . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: ');
header('Pragma: ');
header('Content-Length: ' . filesize($file_path));
flush();
ob_clean();
readfile($file_path);
exit;
} else { exit("No file present."); }
?>
Please help as I have been at this all day and am confused to no end why this won't work. Filesize() pulls the correct length so I know there is a file in the path that I am looking at. (I am also new to PHP, so if there is something that I am missing, please share.)
Thanks in advance!
If it's a big file, it cannot be sent with readfile. Try to use this:
$handle = fopen($file_path, 'rb');
$buffer = '';
while (!feof($handle)) {
$buffer = fread($handle, 4096);
echo $buffer;
ob_flush();
flush();
}
fclose($handle);
I am not sure why this worked, but I was able to solve this issue by breaking my php file into two pieces. Piece 1 loads WordPress and performs the logic validation. Then file 1 passes the information over to file 2 to do the download logic and write the header information.
Like I said, I am not sure why this worked, however a friend who knows PHP better than I do said that sometimes if the script takes too long to process then the headers won't take. It is possible that WordPress was hanging the script too long for these headers.
Hopefully this explanation will help someone else who is having this difficulty.
If you are trying to force browser to download the file with the use of
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="your-file.jpg"
but Chrome gives you ERR_FILE_NOT_FOUND and Firefox also fails with "Not found" (strangely Opera seems to work) try adding:
header('HTTP/1.0 200 OK', true, 200);
Chrome was telling me: "Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found."
And Firefox as saying the file did not exist.
Although the same php file was handling a different type of download I was having issues with PNG and ICO, I tried some methods that only displayed the picture but did not prompt for a download box.
Finally I found out thanks to Crazycoolcam, that Wordpress was the issue.
I was including a php to a file I had called "tools.php",
inside of tools.php it had an include to wordpress's main header file,
to remedy the issue I split my tools file into a wordpress version and a non wordpress version and included the wordpress half after it had written the file out.
Just another possibility here as to why its not working. This was the cause for me. Interestingly, file_exists was returning true but no form of serving the file to the public for download was working without having the below set correctly.
PHP has a setting called open_basedir
Make sure this is set correctly relevant to your hosting environment. open_basedir can be edited via php.ini
Part of our web app has a little Ajax method that will load a page in an iFrame or allow you to download it.
We store a bunch of search results from search engines and we have script opens the file containing our info and the search html. We strip out the stuff we don't need from the top (our info) and then we serve that up either by echo'ing the $html variable or putting it in a temporary file and dishing it off to download.
The problem: I load the page in the iFrame and it's loaded in UTF-8 because everything else is. If I download the file manually it is fine and FF tells me the endoding is x-gbk.
I've tried using mb_convert_encoding to no avail. We are using PHP4 on this server.
Thoughts?
EDIT: Code that drives this
f(!isset($_GET['file']) || $_GET['file'] == '')
{
header("location:index.php");
}
$download = false;
if(!isset($_GET['view']) || $_GET['view'] != 'true')
{
$download = true;
}
$file = LOG_PATH . $_GET['file'];
$fileName = end(explode("/", $file));
$fh = fopen($file, "rb");
if(!$fh)
{
echo "There was an error in processing this file. Please retry.";
return;
}
// Open HTML file, rip out garbage at top, inject "http://google.com" before all "images/"
$html = fread($fh, filesize($file));
fclose($fh);
// Need to trim off our headers
$htmlArr = explode("<!", $html, 2);
$htmlArr[1] = "<!" . $htmlArr[1];
if(strstr($file, "google"))
{
$html = str_replace('src="/images/', 'src="http://google.com/images/', $htmlArr[1]);
$html = str_replace('href="/', 'href="http://google.com/', $html);
}
else if(strstr($file, "/msn/"))
{
$html = str_replace('src="/images/', 'src="http://bing.com/images/', $htmlArr[1]);
$html = str_replace('href="/', 'href="http://www.bing.com/', $html);
}
else
{
$html = $htmlArr[1];
}
if(strstr($file, "baidu"))
{
$html = mb_convert_encoding($html, 'utf-8'); // Does not work
}
if($download)
{
// Write to temporary file
$fh = fopen("/tmp/" . $fileName, 'w+');
fwrite($fh, $html);
fclose($fh);
$fh = fopen("/tmp/" . $fileName, "rb");
header('Content-type: application/force-download;');
header("Content-Type: text/html;");
header('Content-Disposition: attachment; filename="' . $fileName . '"');
fpassthru($fh);
fclose($fh);
unlink("/tmp/" . $fileName);
}
else // AJAX Call
{
echo $html;
}
You may want to try iconv() instead of mb_convert_encoding()--it has support for a much broader set of encodings.