On my site, if a user chooses to download a file, I set a session variable called $step to "downloadEnd" and my PhP code downloads a template file using the following code:
if ($step == "downloadEnd") {
$file = "Template.xlsx";
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime);
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file) . "\"");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
readfile($file);
exit();
$step = "nextStep"; // THIS IS THE LINE THAT DOES NOT EXECUTE
}
if ($step == "nextStep") {
// New Information Rendered to the User
}
The above code works, EXCEPT, that the last line of the first does not appear to 'execute'. In other words, after the download is complete, I want the user to see a new page with new text which is in a separate if statement ... if ($step == "downloadStart") ... but it never gets there. I believe it is because I need to somehow 'trick' the server into thinking there has been another user POST from the browser to the server AFTER the file downloads so that PhP iterates through all the 'if' statements and renders the new information to the user. I cannot seem to find a way to either: (i) have PhP trigger a page refresh after the file is done downloading; or (ii) trick the server into thinking it needs to refresh the page once the file is done. Any help would be appreciated.
I should add that I know the exit() at the end stops the PhP script from executing, but if you omit that line of code, the .xlsx file will be corrupted. I should also add that I tried the alternative fopen($file), fread($file), fclose($file) and that too gave me a corrupted .xlsx download, which is a problem others appear to have encountered as evidenced by other posts on Stack Overflow.
I think I see what your problem is. By running exit() you tell PHP to stop what it's doing. Try the following code:
if ($step == "downloadEnd") {
$file = "Template.xlsx";
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime);
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file) . "\"");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
readfile($file);
$step = "nextStep"; // THIS IS THE LINE THAT DOES NOT EXECUTE
}
if ($step == "nextStep") {
// New Information Rendered to the User
}
Also, you might want to look into JavaScript for refreshing the page after a document has downloaded.
Edit
If you are wanting to show this like a "click link if file did not properly download" page would be then the following should work:
if ($step == "downloadEnd") {
$fileURL = "https://example.com/path/to/Template.xlsx";
echo('<script>window.open("' . $fileURL . '", "_blank");</script>');
$step = "nextStep";
}
if ($step == "nextStep") {
// New Information Rendered to the User
}
You can use something like ob_end() or ob_end_clean() but it would be better to put the new information on a separate page and redirect the user there with echo('<script>window.location.replace("new.php")</script>'). Hope that helped! Also, PHP is a server-side language and therefore cannot tell when a file has finished downloading on the client.
Related
I want to dowload as a web-client a photo from my webserver with PHP.
Therefore, I've written the following PHP-Script:
header('Content-Description: File Transfer');
header('Content-Type: image/jpg');
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($filename));
readfile("/var/www/_old/pictures/".$filename);
($filename is the name of the file, like screenshot_03.06.2016-19:36:50.jpg)
The problem is, the content of this downloaded image is not the image itself, it's the code from the current website. The download works fine, but I cannot open the image properly ...
OK.
After some time, I found a working solution.
My problem was, that I wrote the header-commands AFTER some output. According to the php documentation, this is forbidden.
So all I did is this, I created a new .php-file and checked if the session (in which I previously stored the needed information - filename and download-command) was set correct. On the previous page, I received the POST-information, stored it into the session and redirected (also with header-command) to the newly created php-file.
Then I wrote these header-commands:
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($filename).'"');
readfile($filename);
And now, the download works from all browsers (also IE!).
Here is all my code, I hope this could help someone:
1st page (where I receive the POST-infos):
session_start();
...
...
if(isset($_POST["galerie_submitBt"])){
if($_POST["galerie_submitBt"] === "Herunterladen") { //Das Herunterladen muss in einer eigenen Datei passieren, bevor jeglichen Outputs
$_SESSION["download_Screenshot"] = true;
$_SESSION["filename"] = "/var/www/_old/pictures/".$_POST["filename"];
header("Location:modules/php/download_Screenshot.php");
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>...
2nd, new created php-page (where I actually download the image):
session_start();
if(isset($_SESSION["download_Screenshot"])){
if(isset($_SESSION["filename"]) && file_exists($_SESSION["filename"])) {
$filename = $_SESSION["filename"]; //Filename also includes the path! (absolute path)
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($filename).'"');
readfile($filename);
unset($_SESSION["filename"]);
unset($_SESSION["download_Screenshot"]);
}
}
I'm trying to view files (i.e: excel sheets/pdf/images) on browser that are stored in database.
I already wrote a code for downloading the files from the database and it is working but I want to display it in the browser.
Here is the code:
<?php require_once('Connections/databasestudents.php'); ?>
<?php
$id = $_GET['id']; // ID of entry you wish to view. To use this enter "view.php?id=x" where x is the entry you wish to view.
$query = "SELECT fileContent, filetype FROM file where id = $id"; //Find the file, pull the filecontents and the filetype
$result = MYSQL_QUERY($query); // run the query
if($row=mysql_fetch_row($result)) // pull the first row of the result into an array(there will only be one)
{
$data = $row[0]; // First bit is the data
$type = $row[1]; // second is the filename
Header( "Content-type: $type"); // Send the header of the approptiate file type, if it's' a image you want it to show as one :)
print $data; // Send the data.
}
else // the id was invalid
{
echo "invalid id";
}
?>
What happens is that view.php is downloaded and nothing is viewed.
Any suggestions?
According to your code, $row[1] is "the filename". The Content type header should contain the content type instead, i.e. the file mime type, for example:
header('Content-type: application/pdf');
If you want to add a filename:
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename='.$row[1]);
print $data;
Be sure $data is the content of the file, something you can take from readfile() for example.
Read more on the manual: http://php.net/manual/en/function.readfile.php
Keep in mind that while PDF and images are easily viewable by a browser, I think Excel needs some ad hoc plugin for that.
A more complete example right out of the manual, to get you a more thorough idea (not all those headers are necessary, and you should change others accordingly to your code):
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');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
What I want to do is to allow only one download per user (one access to a href) For that I have a variable in the user's table, which I do change when the link has been clicked.
I use "download.php?file=file.xxx" for doing that.
download.php
$file= basename($_GET['file']);
$root = "documents/rece/";
$path= $root.$file;
echo $path;
if (is_file($path))
{
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.$file);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($path));
readfile($path);
}
else
echo "File error";
exit();
?>
I also update the DDBB and that works. After that I can show or hide the link. The problem is that the downloaded file is corrupted and can't be opened. I will use it with pdf or doc, maybe zip.
Could it be because of the path?
PDF files, as far as I know, start with something like this:
%PDF-1.4
Yours start with 4 blank lines plus documents/rece/Form.pdf%PDF-1.4. You're obviously printing it yourself somewhere before the code you've posted.
In magento1.7, I have tried something like below in my custom controller.
public function getPDF()
{
$imagePath=C:\Users\.........;
$image = Zend_Pdf_Image::imageWithPath($imagePath);
$page->drawImage($image, 40,764,240, 820);
.
.
.
$pdf->pages[] = $page;
$pdf->save("mydoc.pdf");
}
There's no error in it. It generates PDF with image but the PDF document is saved in magento folder instead in My downloads folder. After doing some research, I found some following chunk of lines and added them after $pdf->pages[] = $page;.
$pdfString = $pdf->render();
header("Content-Disposition: attachment; filename=myfile.pdf");
header("Content-type: application/x-pdf");
echo $pdfString;
Now it generates PDF in My Downloads folder. When I try to open it. It throws error saying : Adobe reader couldn't open myfile.pdf because it's not either a supported file type or because the file has been damaged............ Do this happens,when we try to open PDF document generated on localhost or there's some other reason. Please let me know, why this error occurs and also provide me a solution to resolve it.
your problem is probably due to calling both save() and render() together.
save() actually calls render(), the issue could be due to trying to render the PDF twice.
This is also a waste of resources, if you need to save the file it's probably best to just save the file first, and then serve this file directly to the user.
You can do this in plain old PHP (using passthru or readfile), although there's ways to do this inside Zendframework which are better you can look into :)
// .. create PDF here..
$pdf->save("mydoc.pdf");
$file = 'mydoc.pdf';
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');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
?>
if your code is inside a Magento Controller:
$this->getResponse()
->setHttpResponseCode(200)
->setHeader('Pragma', 'public', true)
->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true)
->setHeader('Content-type', $contentType, true)
->setHeader('Content-Length', filesize($file))
->setHeader('Content-Disposition', 'attachment; filename="'.$fileName.'"')
->setHeader('Last-Modified', date('r'));
$this->getResponse()->clearBody();
$this->getResponse()->sendHeaders();
$ioAdapter = new Varien_Io_File();
if (!$ioAdapter->fileExists($file)) {
Mage::throwException(Mage::helper('core')->__('File not found'));
}
$ioAdapter->open(array('path' => $ioAdapter->dirname($file)));
$ioAdapter->streamOpen($file, 'r');
while ($buffer = $ioAdapter->streamRead()) {
print $buffer;
}
$ioAdapter->streamClose();
exit(0);
I was wondering how I could start generating temporarily download links based on files from a protected directory (e.g. /downloads/). These links need to be valid until someone used it 5 times or so or after a week or so, after that the link shouldn't be accessible anymore.
Any help would be appreciated.
One clever solution I've stumbled upon lately if you're using apache (or lighty) is to use mod_xsendfile (http://tn123.ath.cx/mod_xsendfile/), an apache module that uses a header to determine which file to deliver to the user.
It's very simple to install (see link above), and afterward, just include these lines in your .htaccess file:
XSendFile on
XSendFileAllowAbove on
Then in your php code, do something like this when you want the user to receive the file:
header('X-Sendfile: /var/notwebroot/files/secretfile.zip')
Apache will intercept any response with an X-Sendfile header, and instead of sending whatever content you output (you may as well return a blank page), apache will deliver the file.
This takes out all the pain of dealing with mimetypes, chunking, and miscellaneous headers.
Use a database. Every time a file is downloaded the database would be updated, as soon as a certain file has reached it's limit it can be either removed or it's access could be denied. For example:
$data = $this->some_model->get_file_info($id_of_current_file);
if ( $data->max_downloads <= 5 )
{
// Allow access to the file
}
I generally keep files outside of the website directory structure for security and request like so:
function retrive_file($file_hash)
{
$this->_redirect();
$this->db->where('file_hash', $file_hash);
$query = $this->db->get('file_uploads');
if($query->num_rows() > 0)
{
$file_info = $query->row();
if($file_info->protect == 1){
$this->_checklogin();
}
$filesize = filesize($file_info->file_path . $file_info->file_name);
$file = fopen($file_info->file_path . $file_info->file_name, "r");
// Generate the server headers
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$file_info->file_name.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".$filesize);
}
else
{
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="'.$file_info->file_name.'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".$filesize);
}
if($file)
{
while(!feof($file)){
set_time_limit(0);
echo fread($file, $filesize);
flush();
ob_flush();
}
}
fclose($file);
}
}
It would be pretty trivial to add byte/request counting to this.