error using readfile php function - php

I have this function which outputs an image to the browser. However i get an error image cannot be displayed because it contains errors.
I've done the file_exists check and the image headers are all applied correctly.
function sendImage(string $file, int $browser_cache = 2592000) {
if (file_exists($file)) {
header("Content-Type: ". mime_content_type($file));
header("Cache-Control: private, max-age=".$browser_cache);
header('Expires: '.gmdate('D, d M Y H:i:s', time()+$browser_cache).' GMT');
header('Content-Length: '.filesize($file));
readfile($file);
exit;
} else {
echo 'file does not exist';
}
}
Call the function
$file = $_SERVER['DOCUMENT_ROOT'] . '/images/image.png';
sendImage($file);
The image opens in my browser just fine. But displays an error with this function.

What I can tell from experience is that readfile does not work with large files.
And sometimes, even when you got the PHP part right there are other points of failures.
I suggest using the X-Sendfile header instead of manually outputting the file:
function sendImage(string $file, int $browser_cache = 2592000) {
$path = realpath($file);
if ($path !== false) {
header("Content-Type: ". mime_content_type($file));
header("Cache-Control: private, max-age=".$browser_cache);
header('Expires: '.gmdate('D, d M Y H:i:s', time()+$browser_cache).' GMT');
header('Content-Length: '.filesize($file));
header('X-Sendfile: '.$path);
exit;
} else {
echo 'file does not exist';
}
}
Of course you have to enable it in your webserver configuration for it to work.

Related

how to download a file with php and angular

I already uploaded a file and saved the path into my database, but the problem now is that I can't download it. I have already tested the PHP code and it works, but when I try to download it from the Angular side it doesn't. Instead I get this error in the console:
httpErrorResponse error: SyntaxError: Unexpected token p in JSON at position 0
at JSON.parse (<anonymous>)
at XMLHttpRequest.onLoad (localhost:4200/vendor.js:164415:51)
at ZoneDelegate.invokeTask (localhost:4200/polyfills.js:9799:31)
.........
ANGULAR SERVICE:
public getjointes(filepath:string){
const params = new HttpParams()
.set('filepath', filepath);
return this.http.get(url+'/download.php', {params: params });
}
TYPESCRIPT / function
telecharger(path:string) {
console.log(path);
this.stream.getjointes(path).subscribe(response => {
console.log(response);
});
}
PHP CODE:
$rest = $_GET['filepath'] ;
echo($rest);
$filepath = 'upload/' . $rest;
if (file_exists($filepath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($filepath));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize('upload/' . $rest));
readfile('upload/' . $rest);
header("Access-Control-Expose-Headers: Content-Disposition");
}
else{
echo ('noo');
}
Ok I'm going to assume the echo statements in your PHP are just for testing, based on the error messages you included in comments.
I think the problem you're having is that Angular assumes the response will be JSON and attempts to parse it. To avoid this behaviour, you can set the responseType parameter to "text" or "blob" (for binary data) in your code:
public getjointes(filepath:string) {
const params = new HttpParams()
.set('filepath', filepath);
return this.http.get(url+'/download.php', {params: params, responseType: "text"});
}
Note that any output before a call to header() will result in the HTTP header not getting set. Your PHP code calls header() after readfile(). That header will be ignored. In addition, if a file is not found you should say so with a proper 404 header. This way your client-side code can respond properly to failures. Your PHP should look like this:
$rest = $_GET['filepath'];
$filepath = 'upload/' . $rest;
if (file_exists($filepath) && is_readable($filepath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($filepath));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize('upload/' . $rest));
header("Access-Control-Expose-Headers: Content-Disposition");
readfile('upload/' . $rest);
} else {
header("HTTP/1.0 404 Not Found");
}

Force 'save as' svg files

I've got the following file:
download.php
<?php
$filename = $_GET["filename"];
if ($filename) {
$path = "/svg_files/".$filnavn."";
header('Content-Transfer-Encoding: binary'); // For Gecko browsers mainly
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($path)) . ' GMT');
header('Accept-Ranges: bytes'); // For download resume
header('Content-Length: ' . filesize($path)); // File size
header('Content-Encoding: none');
header('Content-Type: application/svg'); // Change this mime type if the file is not PDF
header('Content-Disposition: attachment; filename=' . $filename); // Make the browser display the Save As dialog
}
?>
It's located in the svg_files folder. But when I run:
myweb.com/svg_files/download.php?filename=file.svg
I just get an empty file.
You'll have to output the content of the file to the browser..
Use readfile or file_get_contents and echo or something similar.
As a sidenote: Be aware of so called directory traversal attacks. You might wanna take some precautions.
http://en.wikipedia.org/wiki/Directory_traversal_attack

PHP - simple download script

I'm making a simple download script, where my users can download their own images etc.
But I'm having some weird problem.
When I've downloaded the file, it's having the contents from my index.php file no matter what filetype I've downloaded.. My code is like so:
$fullPath = $r['snptFilepath'] . $r['snptFilename'];
if (file_exists($fullPath)) {
#echo $fullPath;
// setting headers
header('Content-Description: File Transfer');
header('Cache-Control: public'); # needed for IE
header('Content-Type: '.$r['snptFiletype'].'');
header('Content-Disposition: attachment; filename='. $filename . '.' . $r['snptExtension']);
header('Content-Length: '.$r['snptSize'].'');
readfile($fullPath)or die('error!');
} else {
die('File does not exist');
}
$r is the result from my database, where I've stored size, type, path etc. when the file is uploaded.
UPDATE
When I'm uploading and downloading *.pdf files it's working with success. But when I'm trying to download *.zip and text/rtf, text/plain it's acting weird.
By weird I mean: It downloads the full index.php file, with the downloaded file contents inside of it.
ANSWER
I copied this from http://php.net/manual/en/function.readfile.php and it's working now. It seems that : ob_clean(); did the trick! Thanks for the help everyone.
#setting headers
header('Content-Description: File Transfer');
header('Content-Type: '.$type);
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
Try this function , or implement these headers to your code
function force_download($filename) {
$filedata = #file_get_contents($filename);
// SUCCESS
if ($filedata)
{
// GET A NAME FOR THE FILE
$basename = basename($filename);
// THESE HEADERS ARE USED ON ALL BROWSERS
header("Content-Type: application-x/force-download");
header("Content-Disposition: attachment; filename=$basename");
header("Content-length: " . (string)(strlen($filedata)));
header("Expires: ".gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y")))." GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
// THIS HEADER MUST BE OMITTED FOR IE 6+
if (FALSE === strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE '))
{
header("Cache-Control: no-cache, must-revalidate");
}
// THIS IS THE LAST HEADER
header("Pragma: no-cache");
// FLUSH THE HEADERS TO THE BROWSER
flush();
// CAPTURE THE FILE IN THE OUTPUT BUFFERS - WILL BE FLUSHED AT SCRIPT END
ob_start();
echo $filedata;
}
// FAILURE
else
{
die("ERROR: UNABLE TO OPEN $filename");
}
}
I copied this from http://php.net/manual/en/function.readfile.php and it works now. ob_clean(); did the trick..
#setting headers
header('Content-Description: File Transfer');
header('Cache-Control: public');
header('Content-Type: '.$type);
header("Content-Transfer-Encoding: binary");
header('Content-Disposition: attachment; filename='. basename($file));
header('Content-Length: '.filesize($file));
ob_clean(); #THIS!
flush();
readfile($file);

Download image with Zend - PHP header: wrong file format

First of all, I know this question has already been asked but I can't solve it anyway.
I need to set a link to download images(jpg).
I read various posts found here and with google but it's always the same results:
I can download the file but it's still the same error. The jpeg format is not correct.
Erreur d'interprétation du fichier
d'image JPEG (Not a JPEG file: starts
with 0x0a 0x20)
When I test this in a file without a controller, it's ok but the script in a controller doesn't work.
Here is the code for tests:
$file = '{document_root}/www/themes/default/images/common/background1.jpg';
if (file_exists($file))
{
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($file));
header('Content-length: ' . filesize($file));
readfile($file);
exit;
}
This code works in a simple php file. I download the picture and can open it.
But within my controller, the file is not good.
I found that the tag ?> can add spaces but my controllers doesn't have this closing tag.
I've tested some code with the Zend objects found in various posts but it's the same error.
I've tried various way to read the file (file_get_content(), fread() ...) with the same result.
I assume there's something wrong with my Zend controller.
I'm now testing my file according to this post:
php file download: strange http header
Any clue will be really appreciated.
Thanks for your help and sorry for my bad english.
[EDIT: 21/06/2011 - 6h38]
Here is the code of the action
public function downloadAction()
{
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$img = $this->_getParam('img');
// Process the file
$config = Zend_Registry::get('config');
$width = $config->catalog->image->original->maxWidth;
$height = $config->catalog->image->original->maxHeight;
$prefix = $width . 'x' . $height . '_';
$filename = $prefix . $img;
$file = Zend_Registry::get('document_root') . '/data/images/catalog/products/' . $this->_getParam('pid') .'/'. $filename;
if (file_exists($file))
{
$this->getResponse()
->setHeader('Content-Disposition', 'attachment; filename='.$filename)
->setHeader('Content-Transfer-Encoding', 'binary')
->setHeader('Content-Length', filesize($file))
->setHeader('Content-type', 'image/jpeg');
$this->getResponse()->sendHeaders();
readfile($file);
exit;
}
}
This action is not called directly. I test if a parameter exists in the url.
If true then from the listAction, I call the downloadAction().
I've tried to disable the view and layout in both action but there's some html rendered.
I had the same problem sending content after decrypt file's content.
0x0a means new line. You probably have some new line after the ?> tag in some included class.
Put
ob_clean();
flush();
before
readfile($file);
something like this:
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($file));
header('Content-length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
This work out fine for me. Hope it helps.
Regards
I codeing working zend framework :)
public function dowloadfileAction(){
$this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout->disableLayout();
if ($this->_user->isUserLogin()) {
$path_file = 'public/uploads/file/';
$filename = $this->_getParam('file');; // of course find the exact filename....
$file = $path_file.$filename;
//zfdebug(mime_content_type($file)); die();
if (file_exists($file)) {
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false); // required for certain browsers
header('Content-Type: '.mime_content_type($file));
header('Content-Disposition: attachment; filename="'. basename($file) . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file));
readfile($file);
}else{
echo "File does not exist";
}
}else{
echo "Please Login";
}
exit;
}

Force download working, but showing invalid when trying to open locally

I wrote this function and everything works well till i try to open the downloaded copy and it shows that the file is invalid. Here is my function
function download_file() {
//Check for download request:
if(isset($_GET['file'])) {
//Make sure there is a file before doing anything
if(is_file($this->path . basename($_GET['file']))) {
//Below required for IE:
if(ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
//Set Headers:
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $this->path . basename($_GET['file'])) . ' GMT');
header('Content-Type: application/force-download');
header('Content-Disposition: inline; filename="' . basename($_GET['file']) . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($this->path . basename($_GET['file'])));
header('Connection: close');
readfile($this->path . basename($_GET['file']));
exit();
}
}
}
EDIT: By invalid for example I download a picture and try to view it in iPhotos or Windows Picture Viewer and it says, File Format Unsupported. When I view it on the server it looks fine but after download it is corrupt.
Thanks Gumbo, tried that and it outputted:
Warning: gmdate() expects
parameter 2 to be long, string given
in C:\Program Files\Wamp
Server\www\TutToasterUpload\PHPClass.php
on line 83 lets see what
happens
Fixed this line:
//Added filemtime();
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($this->path . basename($_GET['file']))) . ' GMT');

Categories