PHP header read file and redirect - php

I'm trying to redirect my user after they download my files. I manage to make them download the file but I cant redirect them to the url. Pleas help
<?php
//Search for file using GET
if(isset ($_GET['file']) && ($_GET['url'])){
$file = $_GET['file'];
$link = $_GET['url'];
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
} else {
echo "Why are you here?";
}
//file downloaded now go to link
header('Location: $link');
}
?>
How do I fix this?

No, it's not possible what you do and want.
your redirect will be executed if the GET parameters file and url exist and the physic file not exists.
if GET parameters file and url exist and the physic file exists, you exit after the download (script stops with further execution).
Why not a redirect from html code to your download script. Many downloads site work with.
As pseudo, redirect.html:
html
head
redirect to your forced download file FOO after X seconds with e.g. meta tag
/head
body
Download of file FOO starts in X seconds
/body
/html
In the case above you don't need the php redirect, only the force download. Set the redirect script not in the same file as your download script!

remove exit; from true part and try to put this instead
header('Location: $link');

You got the wrong quotes:
header("Location: $link"); // note the "
Or do concatenation:
header('Location: '.$link);
By the way, the way you're doing what you want to do is basically a good way to let anyone with HTTP 80 access to this script to download any file on your server. What if $file = '/etc/passwd';?

Related

php download zip folder as attachment, folder is not found

I'm at a bit of a loss as to why this folder is not being found. I have a script that, after searching a database to find the $filename of someone's purchase based on a stored random code, should simply return their file. My code looks like this (including the trailing end of the db query):
$stmt_2 -> bind_result($filename);
$stmt_2 -> fetch();
$stmt_2 -> close();
// For .zip files
$filepath='/media-files/Label/' . $filename;
if (headers_sent()) {
echo 'HTTP header already sent';
} else {
if (!is_file($filepath)) {
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
echo 'File not found.';
} else if (!is_readable($filepath)) {
header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden');
echo 'File not readable.';
} else {
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . basename($filepath) . '"');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
}
}
When I run this code, I receive "File not found." so !is_file($filepath) is where it is getting tripped up -- However, the path is correct and the zip is definitely there, so I'm not sure what is wrong here.
In terms of debugging, I've tried removing the checks, going directly to the headers and readfile, which returns an empty zip folder. What does work is if I navigate directly to the file by URL...
UPDATE
The file path issue has been fixed, but I am still not able to download the file. In all attempts I get either ERR_INVALID_RESPONSE or if I try to brute force download the file, it returns an empty file. I tried using these headers with no success:
header_remove();
ob_end_clean();
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
ob_end_flush();
exit;
They are large audio files, which appears to be causing the issue...
You have two types of pathes:
(a) The path of an URL. You have a web-adress which defines the root of your webpage.
e.g. https://www.stackoverflow.com is the start of the site. If you adress /questions at this site you always have the path https://www.stackoverflow.com/questions
(b) The path of the drive where the webpage is located. It is the filesystem-root.
e.g. /home/httpd/html/MyWebPage/questions
If you try to use /questions in (b) it will fail because you need the whole path.
So, this said you need to know where '/media-files/Label/'.$filename is located. It seems to me that /media-files is not at root-level of your filesystem (b).
Maybe it is at the web-root but this is not enough for your system to find the file. Therefore you need something like this:
'/root/httpd/MyWebPage/media-files/Label/'.$filename
Nico Haase was absolutely correct, this is an issue with misunderstanding of paths. Here is a link to an article that should clear things up:
https://phpdelusions.net/articles/paths
Currently your script is trying to find the file in:
/media-files/Label/file.zip
not:
/var/www/myproject/media-files/Label/file.zip
The linked article should provide you with all the neccesary information.
TLDR;
use:
$filepath=$_SERVER['DOCUMENT_ROOT'].'/media-files/Label/' . $filename;
UPDATE
With the file size issue it might be that PHP runs out of allowed memory when trying to load the whole file. We could try something like:
flush();
$file = fopen($filepath, "r");
while(!feof($file)) {
// send the current file part to the browser
print fread($file, round(10 * 1024));
// flush the content to the browser
flush();
}
fclose($file);
There are some issues with flush() but it's a good shot I think. You can have a read on: https://www.php.net/manual/en/function.flush
Other then that there is always the possibility to split the file into smaller chunks.

using the browser prompt to download a file, within Wordpress

I am creating a csv file to download, using a technique similar to using the browser prompt to download a file
$csv_export = .... csv data...;
header("Content-type: text/x-csv");
header("Content-Disposition: attachment; filename=" . $csv_filename . "");
echo($csv_export);
exit;
The file downloads just fine. But this is in WordPress, so when I do a php exit; everything stops and the page appears to hang, and the user needs to do a page reload.
Is there any way to do this and/or force a reload so that it does not appear to user to have hung?
Thanks
First of all, why do you use exit in the first place to stop the processing? You could output your content like this too:
clearstatcache();
header('Content-Type: '.$type);
header("Content-Disposition: attachment; filename=".$filename);
header('Content-Length: ' . filesize($path));
readfile($path);
If you really want the processing to stop and didn't send any HTML content you can just use the header redirect:
header("Location: http://example.com/myOtherPage.php");
die();
If you already send content, you could use JavaScript to reload, but I can't give you an example without more context/code.

php download from server

I am trying to download files from server that I uploaded via html form.
If I try to download files that I had uploaded everything works good but if I try to download other people's files they are broken (0 kb size).
The download link is generated by a page for every user:
$_SESSION['u'] = $username;
echo ''.$entry.'';
If I echo($_SESSION['u']) I have the right username referred to user which I want to get the files.
And download.php is:
session_start();
$u = $_SESSION['u'];
$file = basename($_GET['file']);
$percorso = '/home/fosco/documents/'.$u.'/'.$file;
if(!$file){ // file does not exist
die('file not found');
} else {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($percorso).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($percorso));
readfile($percorso);
exit;
}
Where is the mistake?
I don't understand why I am able to download certain file and not other.
I also tried changing permissions to 777 but nothing changed.
Moveing files to public_html, if I go to the public url the files are not broken but anyway I am not able to download it trought download.php
If I download the broken file from ftp it is ok, not broken.
Thanks for the help.
Fosco
I'm guessing that the file you request does not actually exists.
Your if-statement only checks if the variable $file is not false/empty/zero.
For checking if a file exists use the function file_exists()

Remove include file in php script

I have a php file to handle file downloads in a website. It is working as it should.
if($_GET['type'] == "pdf") {
$dir = __DIR__ . "/../src/files/pdf/" . $_GET['name'];
if(file_exists($dir)) {
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename='.basename($dir));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($dir));
readfile($dir);
exit;
} else {
$_SESSION['err'] = "No file found";
header("location: index.php");
exit;
}
}
Now I want to count the times that a file is being downloaded. So I have a method inside a class in a different php file. I usually include this php file and create an instance of this class when I need to use some of its methods. The problem is that if I include the file in my file-handler.php the header will be different and the file would be corrupted when downloaded.
So, how can I use the method of this file in the file-handler without affecting the header?
If you include your counting file before at the top of file-hander.php file before you output your download headers then the download headers will replace whatever headers were output before.
Additionally. your counting file must not output any content at all as the client is expecting the file you are downloading and nothing else.
I agree 100% with #rjdown, your code creates a massive potential security issue. I would not put it on a server connected to an untrusted network (Like The Internet). If you would appreciate help making it secure then please ask that question.

How can I create a download link of a PDF that does not require a right click?

I am working on a website where the visitor should be able to download a pdf file.
(There are three links to choose from but that is irrelevant)
I wanted to know how to make it so that the visitor can simply click the link and not have to
right click > Save (target) As...
I am open to PHP and or Javascript solutions. Thanks.
EDIT: Can I use javascript to call the PHP and save the file via AJAX?
EDIT2: I used Nirmal's solution in the end, since it was the simplest to change for all three files. I didn't need to make 3 files for the three PDF's and I didn't need to hand code the switch. BalusC gets the check though since his/her code was up first and does the trick too.
All you basically need to do is to set the Content-Disposition header to attachment to get a 'Save As' dialogue. Here's a kickoff PHP example:
<?php
header('Content-Type: application/pdf');
header('Content-Disposition: attachment;filename="foo.pdf"');
readfile('/path/to/foo.pdf');
?>
You can't and don't want to do this with Javascript.
Important note: due to a poor feature, in MSIE the default filename in 'Save As' dialogue won't be derived from the content-disposition header, it will instead be the last part of the pathinfo in the request URL. To workaround this, append the PDF filename to the link, e.g. http://example.com/pdf/foo.pdf. You can even make use of it in PHP to read the in the pathinfo specified PDF file. Here's a basic example of pdf.php:
<?php
$file_name = $_SERVER['PATH_INFO'];
$file = '/path/to/pdf/files' . $file_name;
if (file_exists($file)) {
header('Content-Type: application/pdf');
header('Content-Disposition: attachment;filename="' . basename($file_name) . '"');
header('Content-Length: ' . filesize($file));
readfile($file);
} else {
header('HTTP/1.1 404 Not Found');
}
?>
This however assumes that you've MultiViews on so that /pdf/ will go through this PHP file, or at least a RewriteRule from /pdf/ to /pdf.php/.
The major advantage of this approach is that you don't need to change the code whenever you want to add a new PDF file or change the PDF file name.
You can even make it more generic by automatically determining and setting the correct content type:
<?php
$file_name = $_SERVER['PATH_INFO'];
$file = '/path/to/all/files' . $file_name;
if (file_exists($file)) {
header('Content-Type: ' . mime_content_type($file_name));
header('Content-Disposition: attachment;filename="' . basename($file_name) . '"');
header('Content-Length: ' . filesize($file));
readfile($file);
} else {
header('HTTP/1.1 404 Not Found');
}
?>
Name it files.php or so and then you have a generic PHP downloader which you can access by for example http://example.com/files/foo.pdf, http://example.com/files/bar.zip, etcetera.
Hope this helps.
Rather than having to write PHP wrapper scripts, if you are using Apache, you can do this with a .htaccess file in the folder containing the PDFs:
<Files *.pdf>
Header set Content-Disposition attachment
</Files>
Apparently some versions of IE / Adobe Reader don't respect the Content-Disposition header. You can work around these with ForceType application/octet-stream
<Files *.pdf>
ForceType application/octet-stream
Header set Content-Disposition attachment
</Files>
If you don't want to mess with server side code and if this is a low priority thing for you, you can use HTML5 download attribute.
Example:
<a href="myfile.pdf" download>Download</a>
However this is not supported by all browser. As per w3schools, following browsers are supported:
Chrome (14+), Firefox(20+), Opera (15+).
You can also specify filename by giving value to download attr:
Download
Try using PHP to serve the file while first sending a header of Content-type: application/octet-stream.
You can add an HTTP header to do that, there is an example in the PHP Docs (See example one)
The following code may help you:
<?php
if(isset($_GET['docid'])){
switch($_GET['docid']){
case '1':
$file = 'complete/path/to/pdf/file1';
break;
case '2':
$file = 'complete/path/to/pdf/file2';
break;
case '3':
$file = 'complete/path/to/pdf/file3';
break;
default:
exit;
}
if(file_exists($file)){
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
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($file);
exit;
}
}
Save this code as a php file (say, download.php) and call it from the link. What the script does is that it reads the pdf file and outputs it to the buffer. The headers will force the disposition as download.
To call the first pdf, href to '/path/to/download.php?docid=1'
To call the second pdf, href to '/path/to/download.php?docid=2'
To call the third pdf, href to '/path/to/download.php?docid=3'
So, you don't need AJAX to do the work.

Categories