PHP - Forcing download of a file issues - php

Im having some major problems with an site im developing, basically whats happening is a user fills out a form, then jquery takes over and posts all the information to sendfile.php, it then is meant to force the user to download a specific file, but its just not doing anything at all and im not seeing any errors either, the file exists.
The code im using is as follows:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-disposition: attachment; filename="http://website.com/wp-content/uploads/2012/02/303lowe-logo.jpg"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
readfile("http://website.com/wp-content/uploads/2012/02/logo.jpg");
Any help would be awesome.

Based on what you said about using jQuery, I assume you are using AJAX to post the form results to the server. I think you will find that you cannot download a file using AJAX.
Perhaps consider doing the AJAX request then redirecting the user to a new page to download the file. If the redirected page serves the file directly, then the user won't even know they have been redirected (the browser will stay on the same page, usually).

Related

How to disable downloading a file from your web without a session variable?

I don't actually now hoy to ask this question, so it may be probably repeated. Let's see: I would like to disable downloading a file from my web without a download script (just using the URL: http://something/file.zip) unless you're registered, with PHP preferably. Yes, it's a very common topic but I haven't found any information! A lot of pages do this, such as uploaded.net. I hope you understand what I'm talking about. Thanks!
First and foremost, don't allow direct access to the file. Store it outside of your web application's root folder, elsewhere on the file system, so that there is no link which can be used to download it. This is because direct access skips any PHP application and interacts only with the web server, which has no knowledge of your application's session values.
Then create a "download" script to serve the file to users. Generally such a script would be given some identifier for the file, something like:
http://yourserver.com/download.php?file=file.zip
(Important: Be very careful how you identify that file. Do not just blindly let users download whatever they want, or they can enter longer paths onto the URL and download any file from your server. Always validate access to things first.)
This would be just like any other PHP script, except that instead of displaying HTML it would return a file. The actual part of outputting the file can be as simple as:
readfile('/path/to/file.zip');
You'd also likely want to set content headers appropriately, etc. A more complete example can be found in the documentation:
<?php
$file = 'monkey.gif';
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;
}
?>

Issue with readfile() and database insert

I have a really weird problem regarding a small piece of code in a CodeIgniter application. Basically, there's a page with links to various PDF files. When a user clicks on the link, the request is parsed by PHP, an observer is notified, writing the click event in the database (activity log), and then the file is outputted by using readfile().
So far, so good. Tested it, it works like a charm. The PDF is outputted for download, and the event is written in the database as it should.
The problem comes when a user clicks on such link, then cancels the download and clicks on another link no later than 9-10 seconds. When that happens, the event is registered in the database twice.
I did triple check of the observers that record the event, but they appear to be fine. Besides, there's a similar function for a video links, only it redirects to another page instead of outputting the file directly, and it works just fine.
After a few hours of scratching my head, I figured there's an issue with the readfile() function, because, if I put a var_dump();die(); or anything that outputs some text before the download and force it to come as text, the download event is recorded only once.
Here's the code in question:
public function downloadPDF($id = NULL)
{
if (($id == NULL) OR (!$this->validateId($id))) {
// redirect with error
}
$item = // code for fetching the PDF properties from the DB
$this->notify('ActivityObserver'); // writes the download event in the DB
$file = '.' . urldecode($item['link']);
$size = filesize($file);
$name = urldecode(basename($file));
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header("Content-Disposition: attachment; filename=\"$name\"");
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));
readfile($file);
exit();
}
Tried to test it with different browsers, the behaviour is the same. All inspector tools show only 1 request being made on click.
What am I missing in this big ugly picture? Why could it sometimes write twice instead of only once?
Thanks for your time to read this wall of text.

Issues with redirect after file download

I'm building a PHP application, one section of which will export an Excel file when a user submits the last of three pages of HTML forms. This takes a while to process, so on form submit I'm bringing up a "Please Wait" popup with JavaScript prior to processing beginning. The file is then created and downloaded on the users machine by setting the headers below
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="' . $fileName . '.xls' .'"');
header('Cache-Control: max-age=0');
The problem I'm having, is that I then need to redirect the user back to the first page of the form. I can't echo a javascript location.href call, as the content-type has already been changed before this, so it never makes it to screen. Neither can I use a standard PHP header('Location: x') redirect for the same reason.
My question is, after diverting output to a file in the way above, how can I then either get the output back to screen to echo a JavaScript redirect, or redirect the user to a new page in some other way?
As always, any help is much appreciated.
James
Try this code
<?php
echo "<script>
window.location='page.php';
</script>";
?>
As described above there's no way to send new headers after the page has loaded, but there is a way to get the functionality I wanted.
The answer was to create an AJAX call, running in a setInterval loop which looks for a $_SESSION variable in the PHP. This session variable is set after the Excel file is created (where I was previously trying to place the redirect), causing the AJAX function to return success, and then perform a location.href redirect to the correct page.
the below code can do it. it can redirect you last page from where the download request is sent no need to echo content only readfile can do it so the page will automatically unload after download box appear.
$path is the path of your file to make user download
header("Content-type: application/vnd.ms-excel");
header('Content-Disposition: attachment; filename="'.$path.'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header("Cache-Control: public");
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($path));
readfile($path);

Determining successful download using php readfile

I need to know if a user selected download then clicked the cancel button, which is not the same as readfile having an error. I have inspected the count returned by the readfile function, but it shows the bytes in the file even if the user canceled the download from the Save As dialog.
The reason this is needed is because my site has a one-time download, where a member gives permission for another use to download their file one time, then the permission goes away. But if a member clicks the download button then decides not to download it right then, I dont' want my database to get updated to show they got the file.
This deals with intellectual property protection since the files are the property of the member who uploaded them, and I need to keep an audit trail of exactly what other members downloaded the file in case they start floating around the internet. But if the readfile function always reflects the filesize (meaning those bytes were transferred in some way), I have no way to know if the file was actually downloaded.
I have seen a number of posts about this subject, but no real solutions to what has to be a frequent need - did they download it or not? Just knowing that they clicked the download button doesn't really say whether they decided to go through with it since the Save As dialog box allows someone to cancel the actual completion of the download.
For completeness, here is my download code up until the readfile function:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=$download_name");
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("sub/$doc_file"));
ob_clean();
flush();
$wasdownloaded = readfile("sub/$doc_file");
I fear the correct answer is "Impossible" - let me explain: You might be able to correctly figure out, when the file has crossed the wire, but you can't figure out reliably, whether the client threw it away or not.
Example (chronological sequence):
A user on MSIE clicks download and is presented with the "Save where" Dialog.
While this dialog is open, the download is started in the background.
The user navigates around in the dialog or simply does nothing (phone rang, he talks)
The background download is finished, your script sees the download as complete
The user clicks on "cancel"
MSIE deletes the tempfile, the download is never stored in a user-accessible form
Result:
The user sees the file as "not downloaded" - and he is correct
Your app sees the file as "correctly downloaded" - and it is correct
You would first need ignore_user_abort().
This would allow your script to continue on after the user has hit cancel, or escape.
You would then have to print out the file and continuously check with connection_aborted().
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Disposition: attachment; filename=$download_name");
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("sub/$doc_file"));
ob_clean();
flush();
$fp=fopen("sub/$doc_file","rb");
while(!feof($fp))
{
print(fread($fp,1024*8));
flush();
ob_flush();
if( connection_aborted() )
{
//do code for handling aborts
}
}
Use this comment on php.net : http://www.php.net/manual/en/function.fread.php#72716
On fclose you would be able to determine if file has been downloaded successful, because your are checking if user aborted connection with connection_status()

PHP Headers - Content & Location [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Download a file and redirect…or alternative
Basically, we have a PHP file which outputs a file type using PHP headers.
We want it to work like this:
User clicks link, page opens, download prompt comes up and then the page that prompted the download (the main php page) redirects to another page...
How can I go about doing this? I want it to only redirect once the download prompt has been delivered to the user.
So.. sort of a thank you page.
Can this be done like so:
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public', false);
header('Content-Description: File Transfer');
header('Content-Type: '.$type);
header('Accept-Ranges: bytes');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($file));
/* sleep for say 3 seconds.. */
header('Location: thankyou.php');
exit();
This code was purely written as an example, but how can we create something like this?
So the download prompt pops up, wait say 3 seconds, and then direct the origin page to a thank you page.
HTTP headers are output all at once (save for chunked sending, though I'm a bit hazy on whether HTTP allows that for headers).
This question looks to be similar.
header('Location: thankyou.php');
Will kill your download,
If you sleep you stop every thing and the browser wont receive the file it will just redirect to thank-you.php

Categories