Adding download option to images - php

I have a PHP script that is working fine for displaying all my images in a directy that I upload to. I wand to make a little download button so someone can click the button and download the image. I am making this for my company so people can download our logos.
<?php
// Find all files in that folder
$files = glob('grips/*');
// Do a natural case insensitive sort, usually 1.jpg and 10.jpg would come next to each other with a regular sort
natcasesort($files);
// Display images
foreach($files as $file) {
echo '<img src="' . $file . '" />';
}
?>
I figue I could just make a button and call the href of $file but that would just link to the file and show the image. I am not sure to have it auto download. Any help would be great.

Just add some headers in a download.php file so you can then read the file in like this:
Make sure you sanitize your data coming to the file, you don't want people to be able to download your php files.
<?php
// Find all files in that folder
$files = glob('grips/*');
// Do a natural case insensitive sort, usually 1.jpg and 10.jpg would come next to each other with a regular sort
natcasesort($files);
// Display images
foreach($files as $file) {
echo '<img src="' . $file . '" /><br />Download Image';
}
?>
download.php
$filename = base64_decode($_GET["file"]);
// Data sanitization goes here
if(!getimagesize($filename) || !is_file($filename)){
// Not an image, or file doesn't exist. Redirect user
header("Location: /back_to_images.php");
exit;
}
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment; filename=".basename($filename).";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile($filename);

Related

PHP ReadFile Issues with Spaces

Got a bit of a PHP problem I am stuck on at the moment. I have a file called download.php which gives secure access to file downloads which are stored in a private folder on the server. i.e. 1 level above httpd.
The code looks like this.
$Document = new Documents($DID);
$file = $Document->getfilewithdir();
header("Content-Description: File Transfer");
header("Content-length: " . $Document->getfilesize());
header("Content-type: " . $Document->getfiletype());
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-disposition: attachment; filename=\"" . basename($Document->getfilename()) . "\"");
readfile($file);
exit;
Where
$Document->getfilewithdir() returns something like /Applications/MAMP/htdocs/Portal_QCNW/private/portal_files/filename.docx
and
$Document->getfilename() returns something like filename.docx.
Extra code included:
function setfilename($Val)
{
$this->c_Filename = $Val;
}
function getfilename()
{
return $this->c_Filename;
}
function getfilewithdir()
{
$path_parts = pathinfo($this->geturl());
$file_name = $path_parts['basename'];
return FILELOC . $file_name;
}
This is all fine if there file name has no spaces in, but if there are spaces in the file name i.e. file name.docx or filename (1).docx then the readfile statement returns "failed to open stream". What can I do to get round this and deal with spaces in filenames?
I use https://blueimp.github.io/jQuery-File-Upload/ to upload the file, and there could be something there were I remove spaces at the upload stage.
Any thoughts and advice would be great.
Kind Regards
James
Thanks for your help. I reviewed the code and simplified it. Because I was passing a URL file name it was been formatted as a url rather than a string.
All sorted now.
James

php how to serve file but NOT for download

The code below forces browser to fire a prompt to save/open file (https://kb.wisc.edu/images/group27/13334/open-prompt.PNG) even if it's a image or pdf file.
I want images to be opened as usual, pdf files to be displayed in browser. And of course other files that are not supported by browser like zip, rar, doc, xls etc will fire a save file dialog.
Edit:
My intention is not to block client to save the file of course they can save it that's impossible. I want to serve let say images as PHP files like main.php?file=randomcode (which is stored in database) but not as /images/somefilename.jpg . My code forces client to download it but I want to display it.
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: application/octet-stream");
header("Content-Disposition: attachment; filename=\"" . $filename . "." . $fileinfo["file_extension"] . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($file));
$fp = fopen($file, "r");
if ($fp) {
while (!feof($fp)) {
$cur_data = fread($fp, 1024);
echo $cur_data;
}
} else {
echo "Error: Could not the read file.";
}
Ultimately it's up to the client what to do with the content it receives. One thing you can do is get rid of the Content-disposition header:
header("Content-Disposition: attachment; filename=\"" . $filename . "." . $fileinfo["file_extension"] . "\";");
(Or at least get rid of it conditionally, depending on specific factors about the file.) What this header does is tell the client that the content being returned is a "file" (you even provide a suggested name for the file) and should be treated as such. HTTP has no native concept of "files" so this header exists specifically to identify something as a "file."
By not supplying that header, you're not suggesting to the client that the content is a file. The client may still infer that it's a file and treat it as such (which you can't control), but from your end all you'd be doing is returning the content itself.
Well, apparently you know the file extension so you could do:
if(in_array($fileinfo["file_extension"], array('jpg', 'png', 'gif')) {
// set header for viewing the image
$mime_type = $fileinfo["file_extension"];
if($mime_type == 'jpg') {
$mime_type = 'jpeg';
}
header('Content-Type: image/' . $mime_type);
}
else {
// set headers for downloading the file
}
if the content type is set to octate stream then it will defenetly transfer the file means user will force download it. you have to set content type accordingly to open it in browser
for example if type is image then
header("Content-Type: image/jpg");
header("Content-Type: image/png");
etc.
and if its image or pdf then remove Content-Disposition: header

Download Image link using php

I downloaded this code to use as a download button.
<?
$filename = $_GET["filename"];
$buffer = file_get_contents($filename);
/* Force download dialog... */
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
/* Don't allow caching... */
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
/* Set data type, size and filename */
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($buffer));
header("Content-Disposition: attachment; filename=$filename");
/* Send our file... */
echo $buffer;
?>
The thing is, the name of the file ends up with the whole path in the file name, for example, this code:
<a href="download.php?filename=images/something.jpg">
Ends up with an image named "images_something.jpg"
I'd like to remove the "images_" from the final file name, so far I haven't had any luck.
Thanks for the help!
If you need the file name part without folder name, you have to use basename($filename)
http://php.net/manual/en/function.basename.php
basename()
$filename = basename($path);
p.s
Setting Content-Type several times may not be the best way to force a download. Also, I hope you're sanitizing that $filename argument before you use a file_get_contents.
p.p.s
Use readfile, don't cache it in the memory.
$filename = basename($filename);
header("Content-Disposition: attachment; filename=$filename");
Set your filename to only be the basename?
Don't do it at the top unless you change the variables though so your pathing to it still works.

Need help getting this auto-download header to show the right path

I have a page with mp3s that are playable from a player, and there are also links to download the songs. When the user clicks on the download link, it needs to open the download dialog instead of opening a media player, so it was recommended to me to use this header script:
includes/auto-download.php:
<?php
$path = $_GET['path'];
header('Content-Disposition: attachment; filename=' . basename($path));
readfile($path);
?>
And then on my main page, the link looks like this:
Song Name
I seem to be doing something wrong with my paths, as when I click the link, the download box opens, and I can download a file with the correct name, but it doesn't contain any information.
To elaborate on my file structure, I've got this:
/Patrons (where my main index.php page is with my link
/Patrons/includes (where my auto-download.php script is)
/Patrons/Media/Audio/Date/ (this is where all the songs are)
Any help would be greatly appreciated!
Either change HTML code to this:
Song Name
OR change PHP code to this:
readfile('../'.$path);
$path needs to be the relative path to the file from the web root. with no leading slash. If the files are outside the web root you should use a fullpath (/home/webuser/song_files/song.mp3).
For example $path = 'song_files/'. $_GET['name_of_the_file'];
You should also check if the file does not exist and exit with an error.
Here is an example I made in codeigniter.
function _begin_download($document_data) {
basepath = 'uploads/';
$filepath = basepath . $document_data->filename;
if (file_exists($filepath)) {
header("Content-type: " . $document_data->mimetype);
header("Content-length: " . filesize($filepath));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-disposition: attachment; filename="' . $document_data->filename . '"');
readfile($filepath);
exit();
} else {
exit('File not found.');
}
}

Using Header as download link in PHP

So I am having this problem wit my download link. Basically my previous way of creating download link is to just have a form button with method='link' and the action to be the link to the file. This works for firefox and others but not safari. For some reason, when the user tries to download the file (excel file) from safari, it would just show bunch of ascii characters on the browser (I guess its trying to read it using the browser?). Well, I was looking for another solution and it seems like using header is the way to do it. So now I am tryng to create a form button with method='post' and action='download.php' where there is a hidden field with the link to the file. It looks like this
function showDownloadWithHeader($link){
echo "<form action='download.php' method='post' >";
echo "<input class='downloadButton' type='submit' value='Download Report'>";
echo "<input type='hidden' name='filename' value='$link'>";
echo "</form>";
}
And inside the download.php I would just want the user to be asked to download the file.
<?php
if($_POST['filename'] == '' || empty($_POST['filename'])){
exit;
}
$filename = $_POST['filename']; //the file is 2 folder down. e.g. data/stack/bla.xlsx
$file = $filename;
$extension = end(explode('.', $filename));
error_reporting(E_ALL);
ini_set("display_errors",1);
// echo $filename;
// echo "<br/>";
// echo $extension;
// echo filesize($filename);
// echo "<br/>";
switch($extension){
case 'xls':
$mimeType = 'application/vnd.ms-excel';
break;
case 'xlsx':
$mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
break;
}
// echo $mimeType;
header('Content-Description: File Transfer');
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
    flush();
readfile($filename);
exit;
?>
I saw this solution on php.net under readfile() function but it doesnt seem to be working for me. I am doing this on localhost.
Something like this works fine.
header("Pragma: public", true);
header("Expires: 0"); // set expiration time
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment; filename=".basename($file));
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($file));
die(file_get_contents($file));
If I understand you correctly the script is working as expected, i.e if a browser recognizes mime type:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
Then it SHOWS it in browser (whether or not it displays correctly is something else)
Since you want to FORCE it to download INSTEAD OF showing in browser use the mime type:
'application/octet-stream'
This should work in all browsers and force the download instead of display in browser.

Categories