Forcing file upload in PHP - inside Joomla framework

I have PHP code that runs a query in a database, saves the results to a csv file, and then allows the user to upload the file. The problem is that the csv file contains an HTML page around the actual csv content.

I already read all the related questions, including this one . Unfortunately, my code exists in Joomla, so even if I try to redirect to a page that contains only headings, Joomla automatically surrounds it with its own navigation code. This only happens at boot time; if I look at the csv file that was saved on the server, it does not contain HTML.

Can someone help me to force the actual CSV file to be downloaded as it is on the server, and not how the browser edits it? I tried using the header location, for example:

header('Location: ' . $filename); 

but it opens the file in the browser, and does not format the save dialog.

Here is my current code:

 //set dynamic filename $filename = "customers.csv"; //open file to write csv $fp = fopen($filename, 'w'); //get all data $query = "select c.firstname,c.lastname,c.email as customer_email, a.email as address_email,c.phone as customer_phone, a.phone as address_phone, a.company,a.address1,a.address2,a.city,a.state,a.zip, c.last_signin from {$dbpre}customers c left join {$dbpre}customers_addresses a on c.id = a.customer_id order by c.last_signin desc"; $votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error()); $counter = 1; while ($row = mysql_fetch_array($votes,1)) { //put header row if ($counter == 1){ $headerRow = array(); foreach ($row as $key => $val) $headerRow[] = $key; fputcsv($fp, $headerRow); } //put data row fputcsv($fp, $row); $counter++; } //close file fclose($fp); //redirect to file header("Content-type: application/octet-stream"); header("Content-Disposition: attachment; filename=".$filename); header("Content-Transfer-Encoding: binary"); readfile($filename); exit; 

edits The full URL is as follows:

 http://mysite.com/administrator/index.php?option=com_eimcart&task=customers 

with the actual download link looking like this:

 http://mysite.com/administrator/index.php?option=com_eimcart&task=customers&subtask=export 

MORE EDITING Here is a snapshot of the page on which the code is located; the generated file still draws in html for the submenu. Code for selected link (Export as CSV) now

 index.php?option=com_eimcart&task=customers&subtask=export&format=raw 

alt text

Now here is a screenshot of the generated, saved file:

alt text

It is shortened during loading here, but the text highlighted in yellow is the html code for subnav (list of clients, add a new client, export as csv). This is what my complete code looks like now; if I could just get rid of this last html snippet, that would be great.

  $fp= fopen("php://output", 'w'); $query = "select c.firstname,c.lastname,c.email as customer_email, a.email as address_email,c.phone as customer_phone, a.phone as address_phone, a.company, a.address1, a.address2,a.city,a.state,a.zip,c.last_signin from {$dbpre}customers c left join {$dbpre}customers_addresses a on c.id = a.customer_id order by c.last_signin desc"; $votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error()); $counter = 1; //redirect to file header("Content-type: application/octet-stream"); header("Content-Disposition: attachment; filename=customers.csv"); header("Content-Transfer-Encoding: binary"); while ($row = mysql_fetch_array($votes,1)) { //put header row if ($counter == 1){ $headerRow = array(); foreach ($row as $key => $val) $headerRow[] = $key; fputcsv($fp, $headerRow); } //put data row fputcsv($fp, $row); $counter++; } //close file fclose($fp); 

UPDATE FOR BJORN

Here is the code (I think) that worked for me. Use the RAW parameter in the link that invokes the action:

 index.php?option=com_eimcart&task=customers&subtask=export&format=raw 

Since this was procedural, our link was in the customer.php file, which looks like this:

 switch ($r['subtask']){ case 'add': case 'edit': //if the form is submitted then go to validation include("subnav.php"); if ($r['custFormSubmitted'] == "true") include("validate.php"); else include("showForm.php"); break; case 'delete': include("subnav.php"); include("process.php"); break; case 'resetpass': include("subnav.php"); include("resetpassword"); break; case 'export': include("export_csv.php"); break; default: include("subnav.php"); include("list.php"); break; } 

So, when the user clicked on the link above, the export_csv.php file is automatically included. This file contains all the actual code:

 <? header("Content-type: application/octet-stream"); header("Content-Disposition: attachment; filename=customers.csv"); header("Content-Transfer-Encoding: binary"); $fp= fopen("php://output", 'w'); //get all data $query = "select c.firstname,c.lastname,c.email as customer_email, a.email as address_email,c.phone as customer_phone, a.phone as address_phone, a.company,a.address1,a.address2,a.city,a.state,a.zip, c.last_signin from {$dbpre}customers c left join {$dbpre}customers_addresses a on c.id = a.customer_id order by c.last_signin desc"; $votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error()); $counter = 1; while ($row = mysql_fetch_array($votes,1)) { //put header row if ($counter == 1){ $headerRow = array(); foreach ($row as $key => $val) $headerRow[] = $key; fputcsv($fp, $headerRow); } //put data row fputcsv($fp, $row); $counter++; } //close file fclose($fp); 
+6
php joomla download
source share
4 answers

This is the sample code I just prepared to help you. Use it as an action method in your controller.

 function get_csv() { $file = JPATH_ADMINISTRATOR . DS . 'test.csv'; // Test to ensure that the file exists. if(!file_exists($file)) die("I'm sorry, the file doesn't seem to exist."); // Send file headers header("Content-type: text/csv"); header("Content-Disposition: attachment;filename=test.csv"); // Send the file contents. readfile($file); } 

This will not be enough because the file you are loading will still contain the surrounding html. To get rid of it and get only the contents of the csv file, you need to add the format = raw parameter to your request. In my case, the method is inside the com_csvexample component, so the URL will look like this:

 /index.php?option=com_csvexample&task=get_csv&format=raw 

EDIT

To avoid using an intermediate file

 //set dynamic filename $filename = "customers.csv"; //open file to write csv $fp = fopen($filename, 'w'); 

from

 //open the output stream for writing //this will allow using fputcsv later in the code $fp= fopen("php://output", 'w'); 

Using this method, you need to move the code that sends the headers before anything is written to the output. You also do not need to call the readfile function.

+3
source share

Add this method to the controller:

 function exportcsv() { $model = & $this->getModel('export'); $model->exportToCSV(); } 

Then add a new model called export.php, the code below. You will need to modify or extend the code in your situation.

 <?php /** * @package TTVideo * @author Martin Rose * @website www.toughtomato.com * @version 2.0 * @copyright Copyright (C) 2010 Open Source Matters. All rights reserved. * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL */ //No direct acesss defined('_JEXEC') or die(); jimport('joomla.application.component.model'); jimport( 'joomla.filesystem.file' ); jimport( 'joomla.filesystem.archive' ); jimport( 'joomla.environment.response' ); class TTVideoModelExport extends JModel { function exportToCSV() { $files = array(); $file = $this->__createCSVFile('#__ttvideo'); if ($file != '') $files[] .= $file; $file = $this->__createCSVFile('#__ttvideo_ratings'); if ($file != '') $files[] .= $file; $file = $this->__createCSVFile('#__ttvideo_settings'); if ($file != '') $files[] .= $file; // zip up csv files to be delivered $random = rand(1, 99999); $archive_filename = JPATH_SITE.DS.'tmp'.DS.'ttvideo_'. strval($random) .'_'.date('Ym-d').'.zip'; $this->__zip($files, $archive_filename); // deliver file $this->__deliverFile($archive_filename); // clean up JFile::delete($archive_filename); foreach($files as $file) JFile::delete(JPATH_SITE.DS.'tmp'.DS.$file); } private function __createCSVFile($table_name) { $db = $this->getDBO(); $csv_output = ''; // get table column names $db->setQuery("SHOW COLUMNS FROM `$table_name`"); $columns = $db->loadObjectList(); foreach ($columns as $column) { $csv_output .= $column->Field.'; '; } $csv_output .= "\n"; // get table data $db->setQuery("SELECT * FROM `$table_name`"); $rows = $db->loadObjectList(); $num_rows = count($rows); if ($num_rows > 0) { foreach($rows as $row) { foreach($row as $col_name => $value) { $csv_output .= $value.'; '; } $csv_output .= "\n"; } } $filename = substr($table_name, 3).'.csv'; $file = JPATH_SITE.DS.'tmp'.DS.$filename; // write file to temp directory if (JFile::write($file, $csv_output)) return $filename; else return ''; } private function __deliverFile($archive_filename) { $filesize = filesize($archive_filename); JResponse::setHeader('Content-Type', 'application/zip'); JResponse::setHeader('Content-Transfer-Encoding', 'Binary'); JResponse::setHeader('Content-Disposition', 'attachment; filename=ttvideo_'.date('Ym-d').'.zip'); JResponse::setHeader('Content-Length', $filesize); echo JFile::read($archive_filename); } /* creates a compressed zip file */ private function __zip($files, $destination = '') { $zip_adapter = & JArchive::getAdapter('zip'); // compression type $filesToZip[] = array(); foreach ($files as $file) { $data = JFile::read(JPATH_SITE.DS.'tmp'.DS.$file); $filesToZip[] = array('name' => $file, 'data' => $data); } if (!$zip_adapter->create( $destination, $filesToZip, array() )) { global $mainframe; $mainframe->enqueueMessage('Error creating zip file.', 'message'); } } } ?> 

Then go to the default view.php file and add a custom button, for example.

 // custom export to set raw format for download $bar = & JToolBar::getInstance('toolbar'); $bar->appendButton( 'Link', 'export', 'Export CSV', 'index.php?option=com_ttvideo&task=export&format=raw' ); 

Good luck

+3
source share

You can use Apache mod_cern_meta to add HTTP headers to static files. Content-Disposition: attachment . Required .htaccess and .meta can be created by PHP.

0
source share

Another way to output CSV data to a Joomla application is to create a presentation using CSV rather than HTML format. That is, create the file as follows:

components / com_mycomp / view / something / view.csv.php

And add content similar to the following:

 <?php // No direct access defined('_JEXEC') or die; jimport( 'joomla.application.component.view'); class MyCompViewSomething extends JViewLegacy // Assuming a recent version of Joomla! { function display($tpl = null) { // Set document properties $document = &JFactory::getDocument(); $document->setMimeEncoding('text/csv'); JResponse::setHeader('Content-disposition', 'inline; filename="something.csv"', true); // Output UTF-8 BOM echo "\xEF\xBB\xBF"; // Output some data echo "field1, field2, 'abc 123', foo, bar\r\n"; } } ?> 

You can then create links for downloading files as follows:

/index.php?option=com_mycomp&view=something&format=csv

Now you will be correctly asked a question about the built-in part in Content-disposition. If I remember correctly when I wrote this code several years ago, I had problems with the "attachment" option. This link, which I just searched on Google, seemed familiar to me as a driver for it: https://dotanything.wordpress.com/2008/05/30/content-disposition-attachment-vs-inline/ . I have been using "inline" since then, and I am still asked to save the file appropriately from any browsers I'm testing with. I have not tried using "attachment" recently, so now it can work fine (link for 7 years now!)

0
source share

All Articles