Force download prompt php broken file - php

I have now the current code :
if($_POST['mode']=="save") {
$path = $_POST['path'];
$file = end(explode('/', $path));
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=$file");
readfile($file);
}
What i do is, get the path where the file is saved, explode it to only the file name and try to save that file. I do get the save as prompt, but when i save it and try to open the file i get the error message that the file/image is broken.
The files are not saved in the homedirectory but in a subdirectory called uploads.
Someone know what im doing wrong?
Thanks in advance.
Sinan

I normally use something like this:
$siteEmail = 'me#myemail.com';
$companyName = 'My Company';
# Path from server root
$fileDirectory = '/home/uploads/'; // include trailing slash
// get the file reference
$passedFile = $_POST['path'];
$filename = #urldecode($passedFile); // useful if your file is sent by $_GET
if((!isset($passedFile))||(#$passedFile=='')) {
echo "<html><title>".$companyName." - Download File</title><body>ERROR: Please pass a valid filename.<br /><br />Please go back and try again. If you continue to see this message please contact ".$siteEmail."</body></html>";exit();
}
// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
$file_extension = strtolower(substr(strrchr($filename,"."),1));
if( $filename == "" ) {
echo "<html><title>".$companyName." - Download File</title><body>ERROR: download file NOT SPECIFIED.<br /><br />Please go back and try again. If you continue to see this message please contact ".$siteEmail."</body></html>";
exit;
} elseif ( ! file_exists( $fileDirectory . $filename ) ) {
echo "<html><title>".$companyName." - Download File</title><body>ERROR: File not found.<br /><br />Please go back and try again. If you continue to see this message please contact ".$siteEmail."
<p>path: ".$fileDirectory.$filename."</p>
<p>DOCUMENT_ROOT: ".$_SERVER['DOCUMENT_ROOT']."</p>
</body></html>";
exit;
};
switch( $file_extension ) {
case "pdf": $ctype="application/pdf"; break;
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "doc": $ctype="application/msword"; break;
case "xls": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "rtf": $ctype="application/rtf"; break;
case "gif": $ctype="image/gif"; break;
case "png": $ctype="image/png"; break;
case "wav": $ctype="audio/wav"; break;
case "mp3": $ctype="audio/mpeg3"; break;
case "wmv": $ctype="video/x-ms-wmv"; break;
case "avi": $ctype="video/avi"; break;
case "asf": $ctype="video/x-ms-asf"; break;
case "mpg": $ctype="video/mpeg"; break;
case "mpeg": $ctype="video/mpeg"; break;
case "jpeg":
case "jpg": $ctype="image/jpg"; break;
default: $ctype="application/force-download";
}
// Everything went fine - you could log the download in the databse here if required?
// OUTPUT THE FILE
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // cache stop
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // cache stop
header("Cache-Control: must-revalidate"); // cache stop
header("Content-Type: $ctype"); // output filetype
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fileDirectory.$filename));
readfile($fileDirectory.$filename);
exit();

Related

php force download script no longer works

We have been using the following script to download photos but now it no longer works. At the is_file function it redirects like it should if its not a file to the actual image location, which the image is there. If I take that out, it downloads but the jpg has an error. Not sure becuase the image is there (you get redirected to it if it wont downloads).
<?php
ini_set('memory_limit', '-1');
$sourceFile = $_GET['file'];
$sourceFile = urldecode($sourceFile);
if( headers_sent() )
die('Headers Sent');
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
if (!is_file( $sourceFile ))
header( 'Location:'. $sourceFile ) ;
$len = filesize($sourceFile);
$filename = basename($sourceFile);
$file_extension = strtolower(substr(strrchr($filename,"."),1));
switch( $file_extension ) {
case "pdf" : $ctype="application/pdf"; break;
case "exe" : $ctype="application/octet-stream"; break;
case "zip" : $ctype="application/zip"; break;
case "doc" : $ctype="application/msword"; break;
case "xls" : $ctype="application/vnd.ms-excel"; break;
case "ppt" : $ctype="application/vnd.ms-powerpoint"; break;
case ".docx": $ctype="application/vnd.openxmlformats-officedocument.wordprocessingml.document"; break;
case ".pptx": $ctype="application/vnd.openxmlformats-officedocument.presentationml.presentation"; break;
case ".xlsx": $ctype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; break;
case "gif" : $ctype="image/gif"; break;
case "png" : $ctype="image/png"; break;
case "jpeg" :
case "jpg" : $ctype="image/jpg"; break;
case "mp3" : $ctype="audio/mpeg"; break;
case "wav" : $ctype="audio/x-wav"; break;
case "mpeg" :
case "mpg" :
case "mpe" : $ctype="video/mpeg"; break;
case "mov" : $ctype="video/quicktime"; break;
case "avi" : $ctype="video/x-msvideo"; break;
case "mp4" : $ctype="video/mpeg"; break;
//The following are for extensions that shouldn't be downloaded
case "php" :
case "css" :
case "js" :
case "htm" :
case "html" :
case "txt" : die("<b>Cannot be used for ". $file_extension ." files!</b>"); break;
default : $ctype="application/force-download";
}
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: $ctype");
$header="Content-Disposition: attachment; filename=".$filename.";";
header($header );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$len);
echo file_get_contents($sourceFile);
exit;
There are many things that might cause is_file() to fail:
What about your environment changed between when it worked and when it stopped working? Make sure you check the file permissions, including the parent directory.
Does it work with certain images and not others? Might be that your file size is larger than your integer storage. From the manual:
Note: Because PHP's integer type is signed and many platforms use 32bit integers, some filesystem functions may return unexpected results for files which are larger than 2GB.
Does the filename contain any foreign characters? Try removing them.
Have you tried using the full absolute path /var/www/html/images/file.jpg instead of the relative path /images/file.jpg?
Have you tried an alternative function such as file_exists()?
Onto your second problem, the .jpg produces an error even without the file check.
What is the error exactly, or do you mean it downloads corrupted file?
I copy & pasted your code onto my local test environment and it appears to work correctly without any errors. If the information above doesn't help, please update your question with more details.

php readfile bad charset

I have a php code for downloading file based on file_id from URL. All works great, but I have problems with downloading files with extension .doc
Here is my code, what im doing wrong?
$file = mysql_fetch_array(mysql_query("SELECT * FROM ".sqlprefix."files WHERE id = '".$_GET['id']."'"));
$tmp = explode(".",$file['url']);
$tmp = $tmp[count($tmp)-1];
// $tmp = "doc";
switch ($tmp) {
case "pdf": $ctype="application/pdf"; break;
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "docx": $ctype="application/vnd.openxmlformats-officedocument.wordprocessingml.document"; break;
case "doc": $ctype="application/msword"; break;
case "csv":
case "xls":
case "xlsx": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "gif": $ctype="image/gif"; break;
case "png": $ctype="image/png"; break;
case "jpeg":
case "jpg": $ctype="image/jpg"; break;
case "tif":
case "tiff": $ctype="image/tiff"; break;
case "psd": $ctype="image/psd"; break;
case "bmp": $ctype="image/bmp"; break;
case "ico": $ctype="image/vnd.microsoft.icon"; break;
default: $ctype="application/force-download";
}
$baseName = basename($file['url_del']);
$fileSize = filesize($file['url_del']);
$url = $file['url_del'];
// $ctype = "application/msword";
// $baseName = "File name with spaces and diacritic ěščěšč.doc"
// $fileSize = "214016"
// $url = "./files/File name with spaces and diacritic ěščěšč.doc";
header('Content-Description: File Transfer');
header('Content-Type: '.$ctype);
header('Content-Disposition: attachment; filename='.$baseName);
header('Expires: 0');
header('Pragma: public');
header('Content-Length: '.$fileSize);
readfile($url);
exit;
After downloading file in word I get this.
I think, that problem must be in encoding... enter image description here
It was terrible for me too.
I have found the slution here: PHP readfile() causing corrupt file downloads
Try to put this lines before readfile():
//clean all levels of output buffering
while (ob_get_level()) {
ob_end_clean();
}

Download image on click - Using PHP

I'm looking to add a download image on click function for a website. I'm attempting to use PHP to do it as as far as I am aware, there is no way to do it in HTML without it linking to another blank page and then right click save as, which is not what I want.
I found this code on here and wish to adapt it, but I'm concerned of breaking it because my understanding of PHP is not vast. Also, I need to use this on multiple files, but the PHP I'm using suggests you can only use it in one file, meaning I may need numerous PHP files, one for each image download?
Anyway, I'm not sure, but I could do with some help adapting the code if anyone is able to assist me?
The images are in the following directory directly from the site:
/img/bkg/img1.jpg
This also follows into img2-5 also.
Anyway, here is the code used in the main file to show the image and link to the PHP file download:
<a href="download.php?file=path/<?=$row['img1.jpg']?>">
<img src="img/bkg/img1.jpg" alt="CWP - Wallpaper 1" width="1056" height="600"/>
</a>
Here is the code I need to adapt for these files and the correct directories:
<?php
$file = $_GET['file'];
download_file($file);
function download_file( $fullPath ){
// Must be fresh start
if( headers_sent() )
die('Headers Sent');
// Required for some browsers
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
// File Exists?
if( file_exists($fullPath) ){
// Parse Info / Get Extension
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
// Determine Content Type
switch ($ext) {
case "pdf": $ctype="application/pdf"; break;
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "doc": $ctype="application/msword"; break;
case "xls": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "gif": $ctype="image/gif"; break;
case "png": $ctype="image/png"; break;
case "jpeg":
case "jpg": $ctype="image/jpg"; break;
default: $ctype="application/force-download";
}
header("Pragma: public"); // required
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: $ctype");
header("Content-Disposition: attachment; filename=\"".basename($fullPath)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$fsize);
ob_clean();
flush();
readfile( $fullPath );
} else
die('File Not Found');
}
?>
I am sorry if the above is a silly question, but I would rather seek help in this than break it over and over. I am unsure where I am replacing files and directories etc. etc. and I would like to get it right.
Any help is much appreciated.
Your script will handle multiple downloads on one page just fine, you can add as many:
<a href="download.php?file=path/<?=$row['img1.jpg']?>">
to the page as you like. Each time a link is clicked, the script will run and process the download according to the information you sent to it (file=path/<?=$row['img1.jpg']?> in the example).
However, this script has no input validation whatsoever so people can request system file by issuing requests like download.php?file=../../../etc/passwd.
To solve that I would get rid of the path in the query string (you can append that again in php, it's not anybody's business) and use something like a regular expression to check if the sent in variable contains illegal characters.
A simple example to illustrate:
if (preg_match('#[^\w.-]#', $_GET['file']))
{
// if $_GET['file'] contains anything that is not a word character, a dot or a -
die("invalid input");
}
else
{
$file = 'path/' . $_GET['file'];
// rest of your code
I would also remove the mime types you are not using, so only leave jpg or all image types.

php force download using header() broken downloaded file

So firstly i would like to tell that i am quite newbie in header usage and playing with output buffers. So im developing php portlet for liferay and i have some problem with file downloads. I tried to do it simply with 'a href' but problem is that file uploaded via php is unavailable till apache is refreshed so i tried another way with header() function.
So i will try to explain my problem. When i tried following code on simple php project it works fine :
<?php
$path = "/mysecretdir/upload/"; // change the path to fit your websites document structure
$fullPath = $path.$_GET['download_file'];
if ($fd = fopen ($fullPath, "r")) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch($ext) {
case "pdf":
$ctype = "application/pdf";
break;
case "exe":
$ctype = "application/octet-stream";
break;
case "zip":
$ctype = "application/zip";
break;
case "doc":
$ctype = "application/msword";
break;
case "xls":
$ctype = "application/vnd.ms-excel";
break;
case "ppt":
$ctype = "application/vnd.ms-powerpoint";
break;
case "gif":
$ctype = "image/gif";
break;
case "png":
$ctype = "image/png";
break;
case "jpeg":
$ctype = "image/jpg";
break;
case "jpg":
$ctype = "image/jpg";
break;
case "mp3":
$ctype = "audio/mp3";
break;
case "wav":
$ctype = "audio/x-wav";
break;
case "wma":
$ctype = "audio/x-wav";
break;
case "mpeg":
$ctype = "video/mpeg";
break;
case "mpg":
$ctype = "video/mpeg";
break;
case "mpe":
$ctype = "video/mpeg";
break;
case "mov":
$ctype = "video/quicktime";
break;
case "avi":
$ctype = "video/x-msvideo";
break;
case "src":
$ctype = "plain/text";
break;
default:
$ctype = "application/force-download";
}
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-type: " . $ctype);
header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");
header("Content-Transfer-Encoding: binary");
//header("Content-length: $fsize");
header("Cache-control: public"); //use this to open files directly
while(!feof($fd)) {
echo fread($fd, 4096);
flush();
}
}
fclose ($fd);
?>
from index.php im calling it with href="download.php?download_file=/something/"
Ok but now the point. When i am using it in liferay portlet the file is weird. It puts whole generated HTML file in the created file. So its broken. I dunno why. I dont know if headers are sending some information from elsewhere and also dont know how to fix it.
I was searching for hours for some solution but dont know how to make somehow "session" for headers, because i think there is problem with them. Becasue without echo it prints only content of file and in other project - more simple php app not as a part of portal it works! but maybe i am wrong.
So please can someone help me? Any advise?
I can't help you on specific php-portlet problems (I never used Liferay with php) but it sounds like you get the whole page HTML generated "around" your downloaded file. This is what you get when you just render a portlet: A portlet is always embedded in a HTML page, thus you cannot provide specific headers on the HTTP level with the standard render output of a portlet.
What you want is the serveResource lifecycle phase of a portlet. This will allow you to serve content that is not part of the page, but you have full control over the download and HTTP headers. How to do that with php portlets, I'll have to leave to you.
Edit (additional information): As you asked in the comments, I found an older (might need to be adapted) Wiki article that talks about using state=exclusive to do the same trick - instead of the serveResource that I suggested above. I don't know if this is due to the age of the article or because PHP portlets don't support that lifecycle, but you might find something there and in the related&linked articles. Note: serveResource would - if I'm not mistaken - generate a p_p_lifecycle=2 parameter, while this example uses p_p_lifecycle=0 (render) and p_p_state=exclusive. Try if this fits your requirements
However, please consider Marc B's comment about your code being insecure and too hardcoded. There are better solutions for the underlying problem - e.g. Liferay provides the Document Library to up/download files out of the box. And that doesn't have these problems.
If you started your PHP file with a space or any other character, headers won't work and the display/download will fail. Make sure you don't have anything before the <?php tag.
Other than that, this should work (if you're not getting errors in the browser).
On the other hand, if you see the actual PHP code, your server does not support PHP.

How to push Headers from a file on an external server to download?

The following script is what I usually use to push headers to the browser so that the dialog box appears for users to download a file.
However, in this case the file resides on a different server. I though this should make no difference but it does as when I execute this script with the URL of an externam MP3 file it gives me a "ERROR: File not found". However, this file exists, and I can get to it using the same URL I pass to this script.
Any ideas why? I would appreciate any help.
<?php session_start();
//below variable contains full path to external site where file resides
$filename = $_SESSION['$serverURL'].'audio/'.$_SESSION['fileName'].'.mp3';
//below variable contains a chosen filename of the file to be downloaded
$properFilename = $_GET['properFilename'].'.mp3';
// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
// addition by Jorg Weske
$file_extension = strtolower(substr(strrchr($filename,"."),1));
if( $filename == "" )
{
//echo "download file NOT SPECIFIED";
exit;
} elseif ( ! file_exists( $filename ) )
{
//echo "ERROR: File not found";
exit;
};
switch( $file_extension )
{
case "pdf": $ctype="application/pdf"; break;
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "doc": $ctype="application/msword"; break;
case "xls": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "gif": $ctype="image/gif"; break;
case "png": $ctype="image/png"; break;
case "jpeg":
case "jpg": $ctype="image/jpg"; break;
default: $ctype="application/force-download";
}
header("Pragma: public"); // required
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: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($properFilename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();
?>
Single-quotes strings are not parsed for variables, so $_SESSION['$serverURL'] is probably not going to work as you expect. I suspect you mean $_SESSION[$serverURL] or $_SESSION['serverURL'].
Also calling filesize() and then readfile() will probably result in your script making two HTTP requests to fetch the file from the other server (unless this gets cached somehow). You could do it in one HTTP request using cURL, which may be a better option. Here is a brief example, you should be able to adapt it to do what you want. You might also want to consider forwarding other headers such as the Content-Type header from the other server (if reliable) rather than re-generating them yourself.
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://example.com');
//set callbacks to receive headers and content
curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'on_receive_header');
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'on_receive_content');
//send your other custom headers somewhere like here
if (false === curl_exec($ch)) {
//handle error better than this.
die(curl_error($ch));
}
function on_receive_header($ch, $string) {
//You could here forward the other headers received from your other server if you wanted
//for now we only want Content-Length
if (stripos($string, 'Content-Length') !== false) {
header($string);
}
//curl requires you to return the amount of data received
$length = strlen($string);
return $length;
}
function on_receive_content($ch, $string) {
echo $string;
//again return amount written
$length = strlen($string);
return $length;
}

Categories