Force download file after query php mvc - php

I got stuck with the code left by the first programmer of the current webpage that I'm on. He create his own custom framework.
My problem is that I have created a function for file export cause there is a feature wherein I should export the query to textfile so client/user download it automatically via forcing the browser to download the generated file: heres the code of my force download.
Note: I have already search with others forums as well as at Stackoverflow but I can't find an answer. This is my current code:
if(!defined('APPLICATION_RUNNING')){
header("HTTP/1.0 404 Not Found");
die('acces denied');
}
if(defined('ANNOUNCE')){
echo "\n<!-- loaded: ".__FILE__." -->\n";
}
if(!class_exists('APP_Ajax')){
class APP_Ajax extends APP_Base_Ajax
{
function textfiles()
{
global $apptemplate, $appform, $appdb;
if(!$appform->form_valid($this->post['formid'])) // if not b\valid form
{
json_return_error(5);
}
else
{
if(!empty($this->post['form'])) //if the post of form is not empty
{
$e = array();
}
else
{
json_return_error(6);//alert if form is empty
}
if(empty($this->post['form']['rowid']))
{
json_return_error(252); // if rowid is empty then raise error
}
else
{
$rets = $this->post['form']['rowid'];
$ids = explode(',',$rets);
foreach ($ids as $kids)
{
$sql = pg_query("SELECT * from tbl_patient WHERE id='$kids'");
$info = pg_fetch_assoc($sql);
$text[] = ucwords($info['firstname']." ".$info['mi']." ".$info['lastname']).",".$info['medicalrecordnumber'].",".$info['referraldate'];
}
$output = implode( "\r\n" , $text );
$randfile = rand(12345689,999999999)."_".date('m-d-Y');
$f = fopen("templates/default/exports/".$randfile.".txt", "w");
fwrite($f, $output);
fclose($f);
$file_name = ABS_PATH.'templates/default/exports/'.$randfile.'.txt';
header('Pragma: public'); // required
header('Expires: 0');// no cache
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: '.gmdate ('D, d M Y H:i:s', filemtime ($file_name)).' GMT');
header('Cache-Control: private',false);
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename="'.basename($file_name).'"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($file_name));
header('Connection: close');
readfile($file_name);// push it out
}
}
}
}
The code above produce no error, the file where created on the path I create but
the problem is that "No save as" or something, the browser do not download a file. When I use httpfox to view the headers, etc, etc, the result of the query is displayed on httpfox content tab, so nothings wrong with the code except that I want the browser to show the Download file(Save as dialog) so the user can save it to there Hard drives..
Note 2#: When I create a download link like Download, when I click the download link, the page returns me to the main page and no downloads at all.
Please give me a script or advice on how to work on this stuff. Thank you in advance.
Tell me if I'm missing something here.
Oops forgot to tell you, the website I'm on it do not allow external access to file for security that's why I can't link the export file it to other page for download. :)

Related

Page Refresh in PhP After .xlsx Download

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.

View file stored in database using php on browser

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;

Codeigniter Force download files

By going through the Codeigniter documentation, I am using the following code to force download files from my server.
function download($file_id){
$file = $this->uploadmodel->getById($file_id); //getting all the file details
//for $file_id (all details are stored in DB)
$data = file_get_contents($file->full_path); // Read the file's contents
$name = $file->file_name;;
force_download($name, $data);
}
The code is working file for images, but when it comes with the case of PDF files, it is not working. I have not tested it for all file extensions, but since it is not working for PDF, it might not work for other various file types.
Any solution?
I've had similar problems. I think the problem resides in certain mime's and headers sent to the browser(s). I've end up using the code I found here http://taggedzi.com/articles/display/forcing-downloads-through-codeigniter. Use the function below instead of force_download. It has worked for me so far.
function _push_file($path, $name)
{
// make sure it's a file before doing anything!
if(is_file($path))
{
// required for IE
if(ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); }
// get the file mime type using the file extension
$this->load->helper('file');
$mime = get_mime_by_extension($path);
// Build the headers to push out the file properly.
header('Pragma: public'); // required
header('Expires: 0'); // no cache
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: '.gmdate ('D, d M Y H:i:s', filemtime ($path)).' GMT');
header('Cache-Control: private',false);
header('Content-Type: '.$mime); // Add the mime type from Code igniter.
header('Content-Disposition: attachment; filename="'.basename($name).'"'); // Add the file name
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($path)); // provide file size
header('Connection: close');
readfile($path); // push it out
exit();
}
}
Hope it helps.
It is working for .pdfs also.
Check the path to the file - that might be the problem.
I, too, had that problem, but when I corrected the path to the file, it worked perfectly.
The following is how I wrote the code:
if($src == "xyyx")
{
$pth = file_get_contents(base_url()."path/to/the/file.pdf");
$nme = "sample_file.pdf";
force_download($nme, $pth);
}
Try this, I think its best practice for that problem.
function download($file_id)
{
$this->load->helper('file'); // Load file helper
$file = $this->uploadmodel->getById($file_id); //Get file by id
$data = read_file($file->full_path); // Use file helper to read the file's
$name = $file->file_name;;
force_download($name, $data);
}

PHP readfile() causing corrupt file downloads

I am using php script to provide download from my website after a requisite javascript timer this php script is included which causes the download. But the downloaded file is corrupt no matter whatever I try. Can anyone help me point out where am I going wrong.
This is my code
<?php
include "db.php";
$id = htmlspecialchars($_GET['id']);
$error = false;
$conn = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);
if(!($conn)) echo "Failed To Connect To The Database!";
else{
if(mysql_select_db(DB_NAME,$conn)){
$qry = "SELECT Link FROM downloads WHERE ID=$id";
try{
$result = mysql_query($qry);
if(mysql_num_rows($result)==1){
while($rows = mysql_fetch_array($result)){
$f=$rows['Link'];
}
//pathinfo returns an array of information
$path = pathinfo($f);
//basename say the filename+extension
$n = $path['basename'];
//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//Finally it reads the file and prepare the output
readfile($f);
exit();
}else $error = true;
}catch(Exception $e){
$error = true;
}
if($error)
{
header("Status: 404 Not Found");
}
}
}
?>
This helped me in case of more output buffers was opened.
//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//clean all levels of output buffering
while (ob_get_level()) {
ob_end_clean();
}
readfile($f);
exit();
First of all, as some people pointed out on the comments, remove all spaces before the opening PHP tag (<?php) on the first line and that should do the trick (unless this file is included or required by some other file).
When you print anything on the screen, even a single space, your server will send the headers along with the content to be printed (in the case, your blank spaces). To prevent this from happening, you can:
a) not print anything before you're done writing the headers;
b) run an ob_start() as the first thing in your script, write stuff, edit your headers and then ob_flush() and ob_clean() whenever you want your content to be sent to the user's browser.
In b), even if you successfully write your headers without getting an error, the spaces will corrupt your binary file. You should only be writing your binary content, not a few spaces with the binary content.
The ob_ prefix stands for Output Buffer. When calling ob_start(), you tell your application that everything you output (echo, printf, etc) should be held in memory until you explicitly tell it to 'go' (ob_flush()) to the client. That way, you hold the output along with the headers, and when you are done writing them, they will be sent just fine along with the content.
ob_start();//add this to the beginning of your code
if (file_exists($filepath) && is_readable($filepath) ) {
header('Content-Description: File Transfer');
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=$files");
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("content-length=".filesize($filepath));
header("Content-Transfer-Encoding: binary");
/*add while (ob_get_level()) {
ob_end_clean();
} before readfile()*/
while (ob_get_level()) {
ob_end_clean();
}
flush();
readfile($filepath);

temp. download links (with codeigniter)

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.

Categories